From 21b26bbf9b793d69b18c346b4a904fda4d37faaf Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Mon, 20 Sep 2004 07:50:06 +0000 Subject: [PATCH] prepared to contain old internal representation code git-svn-id: http://svn.osgeo.org/postgis/trunk@832 b70326c6-7e19-0410-871a-916f4a2858ee --- hwgeom/Makefile | 247 ++ hwgeom/postgis.h | 716 +++++ hwgeom/postgis.sql.in | 2801 ++++++++++++++++++ hwgeom/postgis_algo.c | 512 ++++ hwgeom/postgis_chip.c | 277 ++ hwgeom/postgis_debug.c | 337 +++ hwgeom/postgis_estimate.c | 1950 ++++++++++++ hwgeom/postgis_fn.c | 3218 ++++++++++++++++++++ hwgeom/postgis_geos.c | 2142 ++++++++++++++ hwgeom/postgis_geos_wrapper.cpp | 1551 ++++++++++ hwgeom/postgis_gist_71.c | 599 ++++ hwgeom/postgis_gist_72.c | 738 +++++ hwgeom/postgis_inout.c | 4906 +++++++++++++++++++++++++++++++ hwgeom/postgis_ops.c | 1123 +++++++ hwgeom/postgis_proj.c | 646 ++++ hwgeom/postgis_svg.c | 352 +++ hwgeom/postgis_transform.c | 513 ++++ 17 files changed, 22628 insertions(+) create mode 100644 hwgeom/Makefile create mode 100644 hwgeom/postgis.h create mode 100644 hwgeom/postgis.sql.in create mode 100644 hwgeom/postgis_algo.c create mode 100644 hwgeom/postgis_chip.c create mode 100644 hwgeom/postgis_debug.c create mode 100644 hwgeom/postgis_estimate.c create mode 100644 hwgeom/postgis_fn.c create mode 100644 hwgeom/postgis_geos.c create mode 100644 hwgeom/postgis_geos_wrapper.cpp create mode 100644 hwgeom/postgis_gist_71.c create mode 100644 hwgeom/postgis_gist_72.c create mode 100644 hwgeom/postgis_inout.c create mode 100644 hwgeom/postgis_ops.c create mode 100644 hwgeom/postgis_proj.c create mode 100644 hwgeom/postgis_svg.c create mode 100644 hwgeom/postgis_transform.c diff --git a/hwgeom/Makefile b/hwgeom/Makefile new file mode 100644 index 000000000..f1bd9a1de --- /dev/null +++ b/hwgeom/Makefile @@ -0,0 +1,247 @@ +#--------------------------------------------------------------- +# Configuration Directives +# +# We recommend that you install the Proj4 and GEOS libraries +# referenced below to get the most use out of your PostGIS +# database. + +#--------------------------------------------------------------- +# Set USE_PROJ to 1 for Proj4 reprojection support (recommended) +# +# Reprojection allows you to transform coordinate systems +# in the database with the Transform() function. +# +# Download from: http://www.remotesensing.org/proj +# +USE_PROJ=1 +ifeq (${PROJ_DIR},) + PROJ_DIR=/usr/local +endif + +#--------------------------------------------------------------- +# Set USE_GEOS to 1 for GEOS spatial predicate and operator +# support (recommended) +# +# GEOS allows you to do exact topological tests, such as +# Intersects() and Touches(), as well as geometry operations, +# such as Buffer(), GeomUnion() and Difference(). +# +# Download from: http://geos.refractions.net +# +USE_GEOS=1 +ifeq (${GEOS_DIR},) + GEOS_DIR=/usr/local +endif + +#--------------------------------------------------------------- +# Set USE_STATS to 1 for new GiST statistics collection support +# Note that this support requires additional columns in +# GEOMETRY_COLUMNS, so see the list archives for info or +# install a fresh database using postgis.sql +# +USE_STATS=1 + +#--------------------------------------------------------------- +subdir=contrib/postgis + +#--------------------------------------------------------------- +# Default the root of the PostgreSQL source tree +# To use a non-standard location set the PGSQL_SRC environment +# variable to the appropriate location. +# +ifeq (${PGSQL_SRC},) + PGSQL_SRC = ../.. +endif + +#--------------------------------------------------------------- +# Path to library (to be specified in CREATE FUNCTION queries) +# Defaults to $libdir. +# Set LPATH environment variable to change it. +# +ifeq (${LPATH},) + LPATH := \$$libdir +endif + +#--------------------------------------------------------------- +top_builddir = ${PGSQL_SRC} +include $(top_builddir)/src/Makefile.global + +#--------------------------------------------------------------- +# Default missing CXX variable to c++ +# +ifeq ($(CXX),) + CXX = c++ +endif + +#--------------------------------------------------------------- +# Test the version string and set the USE_VERSION macro +# appropriately. +# +ifneq ($(findstring 7.1,$(VERSION)),) + USE_VERSION=71 +else + ifneq ($(findstring 7.2,$(VERSION)),) + USE_VERSION=72 + else + ifneq ($(findstring 7.3,$(VERSION)),) + USE_VERSION=73 + else + ifneq ($(findstring 7.4,$(VERSION)),) + USE_VERSION=74 + else + USE_VERSION=80 + endif + endif + endif +endif + +#--------------------------------------------------------------- +# Shared library parameters. +# +NAME=postgis +SO_MAJOR_VERSION=0 +SO_MINOR_VERSION=9 +SO_MICRO_VERSION=0 +SCRIPTS_VERSION=0.0.1 +ifeq (${USE_VERSION}, 71) + MODULE_FILENAME = $(LPATH)/$(shlib) + MODULE_INSTALLDIR = $(libdir) +else + MODULE_FILENAME = $(LPATH)/$(shlib) + MODULE_INSTALLDIR = $(pkglibdir) +endif + +#--------------------------------------------------------------- +# Postgis version +#--------------------------------------------------------------- + +POSTGIS_LIB_VERSION = $(SO_MAJOR_VERSION).$(SO_MINOR_VERSION).$(SO_MICRO_VERSION) +POSTGIS_VERSION = $(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) USE_GEOS=$(USE_GEOS) USE_PROJ=$(USE_PROJ) USE_STATS=$(USE_STATS) + +#--------------------------------------------------------------- + +override CFLAGS += -g -fexceptions +override CFLAGS += -I$(srcdir) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' +override CFLAGS += -DUSE_VERSION=$(USE_VERSION) +override CFLAGS += -DPOSTGIS_LIB_VERSION='"$(POSTGIS_LIB_VERSION)"' +override CFLAGS += -DPOSTGIS_SCRIPTS_VERSION='"$(SCRIPTS_VERSION)"' + +ifeq ($(USE_GEOS),1) + override CFLAGS += -I$(GEOS_DIR)/include -DUSE_GEOS + GEOS_RULES=detect_geos_version +endif +ifeq ($(USE_PROJ),1) + override CFLAGS += -I$(PROJ_DIR)/include -DUSE_PROJ +endif + +override DLLLIBS += $(BE_DLLLIBS) + +override CXXFLAGS := $(CFLAGS) +# memory debug for gcc 2.91, 2.95, 3.0 and 3.1 +# for gcc >= 3.2.2 set GLIBCPP_FORCE_NEW at runtime instead +#override CXXFLAGS += -D__USE_MALLOC + +# this seems to be needed by gcc3.3.2 / Solaris7 combination +# as reported by Havard Tveite +override CXXFLAGS += -fPIC + +#--------------------------------------------------------------- +# Add index selectivity to C flags +# +ifeq ($(USE_STATS),1) + override CFLAGS += -DUSE_STATS +endif + +#--------------------------------------------------------------- +# Select proper GiST support C file +# +ifeq ($(USE_VERSION),71) + GIST_SUPPORT=71 + GIST_ESTIMATE= +else + GIST_SUPPORT=72 + GIST_ESTIMATE=postgis_estimate.o +endif + +ifeq ($(USE_GEOS),1) + GEOS_WRAPPER=postgis_geos_wrapper.o +endif + +OBJS=postgis_debug.o postgis_ops.o postgis_fn.o postgis_inout.o postgis_proj.o postgis_chip.o postgis_transform.o postgis_svg.o postgis_gist_$(GIST_SUPPORT).o $(GIST_ESTIMATE) postgis_geos.o $(GEOS_WRAPPER) postgis_algo.o + +#--------------------------------------------------------------- +# Add libraries that libpq depends (or might depend) on into the +# shared library link. (The order in which you list them here doesn't +# matter.) + +SHLIB_LINK = $(filter -L%, $(LDFLAGS)) +ifeq ($(USE_GEOS),1) + SHLIB_LINK += -lstdc++ -L$(GEOS_DIR)/lib -lgeos +endif +ifeq ($(USE_PROJ),1) + SHLIB_LINK += -L$(PROJ_DIR)/lib -lproj +endif +SHLIB_LINK += $(BE_DLLLIBS) + +#--------------------------------------------------------------- +# Makefile targets + +include $(top_srcdir)/src/Makefile.shlib + +postgis_geos_wrapper.o: postgis_geos_wrapper.cpp + +all: $(GEOS_RULES) all-lib postgis.sql postgis_undef.sql loaderdumper + +loaderdumper: + $(MAKE) -C loader + +# Shared library stuff + +postgis_old.sql: postgis_sql_common.sql.in postgis_sql_$(USE_VERSION)_end.sql.in postgis_sql_$(USE_VERSION)_start.sql.in + cat postgis_sql_$(USE_VERSION)_start.sql.in postgis_sql_common.sql.in postgis_sql_$(USE_VERSION)_end.sql.in | sed -e 's:@MODULE_FILENAME@:$(MODULE_FILENAME):g;s:@POSTGIS_VERSION@:$(POSTGIS_VERSION):g' > $@ + +postgis.sql: postgis.sql.in + cpp -P -traditional-cpp -DUSE_VERSION=$(USE_VERSION) $< | sed -e 's:@MODULE_FILENAME@:$(MODULE_FILENAME):g;s:@POSTGIS_VERSION@:$(POSTGIS_VERSION):g;s:@POSTGIS_SCRIPTS_VERSION@:$(SCRIPTS_VERSION):g' > $@ + +postgis_undef.sql: postgis.sql create_undef.pl + perl create_undef.pl $< $(USE_VERSION) > $@ + +install: all installdirs install-postgis-lib + $(INSTALL_DATA) postgis.sql $(DESTDIR)$(datadir) + $(INSTALL_DATA) postgis_undef.sql $(DESTDIR)$(datadir) + $(INSTALL_DATA) spatial_ref_sys.sql $(DESTDIR)$(datadir) + $(INSTALL_DATA) README.postgis $(DESTDIR)$(datadir) + $(MAKE) DESTDIR=$(DESTDIR) -C loader install + +#- This has been copied from postgresql and adapted +install-postgis-lib: $(shlib) + $(INSTALL_SHLIB) $< $(DESTDIR)$(MODULE_INSTALLDIR)/$(shlib) +ifneq ($(PORTNAME), win) +ifneq ($(shlib), lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION)) + cd $(DESTDIR)$(MODULE_INSTALLDIR) && \ + rm -f lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) && \ + $(LN_S) $(shlib) lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) +endif +ifneq ($(shlib), lib$(NAME)$(DLSUFFIX)) + cd $(DESTDIR)$(MODULE_INSTALLDIR) && \ + rm -f lib$(NAME)$(DLSUFFIX) && \ + $(LN_S) $(shlib) lib$(NAME)$(DLSUFFIX) +endif + +endif # not win +#---------------------------------------------------------- + +detect_geos_version: + ./geos_version.sh $(GEOS_DIR) > postgis_geos_version.h + +installdirs: + $(mkinstalldirs) $(docdir)/contrib $(datadir)/contrib $(libdir) + +uninstall: uninstall-lib + @rm -f $(docdir)/contrib/README.postgis $(datadir)/contrib/postgis.sql + +clean distclean maintainer-clean: clean-lib + @rm -f $(OBJS) postgis.sql postgis_undef.sql postgis_geos_version.h + $(MAKE) -C loader clean + $(MAKE) -C doc clean + diff --git a/hwgeom/postgis.h b/hwgeom/postgis.h new file mode 100644 index 000000000..ed1671f31 --- /dev/null +++ b/hwgeom/postgis.h @@ -0,0 +1,716 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of hte GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.51 2004/09/16 15:50:59 mleslie + * Added the distance_sphere function to calculate the distance between two points + * on an earth-sized sphere using an algorithm implemented by Bruno Wolff III. + * Added the postgresql loader function. + * + * Revision 1.50 2004/08/19 06:15:58 strk + * USE_VERSION gets 80 where it got 75 + * + * Revision 1.49 2004/07/28 13:37:43 strk + * Added postgis_uses_stats and postgis_scripts_version. + * Experimented with PIP short-circuit in within/contains functions. + * Documented new version functions. + * + * Revision 1.48 2004/07/23 21:24:33 strk + * Added postgis_proj_version() + * + * Revision 1.47 2004/07/22 16:20:09 strk + * Added postgis_lib_version() and postgis_geos_version() + * + * Revision 1.46 2004/06/09 09:06:55 strk + * Added Romi's Win32 patches. + * + * Revision 1.45 2004/06/08 15:18:12 strk + * Deleted prototype for isspace() in postgis.h + * and included 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() + * 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 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(,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(,[]). + * + * Added all the functions like PolyFromText(,[]) + * + * 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 +#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 // 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; + * + * 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: + //
+ // NULL when serialized + void *data; // data[0] = bottm left, data[width] = 1st pixel, 2nd row (uncompressed) +} CHIP; + + +//for GiST indexing + +//This is the BOX type from geo_decls.h +// Its included here for the GiST index. +// Originally, we used BOXONLYTYPE geometries as our keys, but after +// Oleg and teodor (http://www.sai.msu.su/~megera/postgres/gist/) +// have released a more generic rtree/gist index for geo_decls.h polygon +// type. I am using a slightly modified version of this, so +// it will be easier to maintain. +// +// Their indexing is based on the BOX object, so we include it here. + + +// ONLY FOR INDEXING +typedef struct geomkey { + int32 size; /* size in varlena terms */ + BOX key; + int32 SRID; //spatial reference system identifier +} GEOMETRYKEY; + + +// WKB structure -- exactly the same as TEXT +typedef struct Well_known_bin { + int32 size; // total size of this structure + unsigned char data[1]; //THIS HOLD VARIABLE LENGTH DATA +} WellKnownBinary; + +// -------------------------------------------- +// histogram2d type + +// 2d histogram is a bounding box with a bunch of cells in it. +// The cells will have width (xmax-xmin)/boxesPerSide +// and height(ymax-ymin)/boxesPerSide +// The first box is the ll corner's box, the send is directly to the right +// (row-major). +// +// Size of structure is: +// 4 (size) + 32 (box) + 4 (boxesPerSide) + +// boxesPerSide*boxesPerSide*4 (data) +typedef struct histotag +{ + int32 size; // postgres variable-length type requirement + int boxesPerSide; //boxesPerSide * boxesPerSide = total boxes in grid + double avgFeatureArea; // average bbox area of features in this histogram + // double will be correctly aligned + double xmin,ymin, xmax, ymax; // BOX of area + unsigned int value[1]; // variable length # of ints for histogram +} HISTOGRAM2D; + + + +//prototypes + + +/* constructors*/ +POLYGON3D *make_polygon(int nrings, int *pts_per_ring, POINT3D *pts, int npoints, int *size); +void set_point( POINT3D *pt,double x, double y, double z); +GEOMETRY *make_oneobj_geometry(int sub_obj_size, char *sub_obj, int type, bool is3d, int SRID,double scale, double offx,double offy); + + +void print_point(char *result, POINT3D *pt,bool is3d); +void print_many_points(char *result, POINT3D *pt ,int npoints, bool is3d); +void swap(double *d1, double *d2); +int numb_points_in_list(char *str); +bool parse_points_in_list(char *str, POINT3D *points, int32 max_points, bool *is3d); +bool parse_points_in_list_exact(char *str, POINT3D *points, int32 max_points, bool *is3d); +int find_outer_list_length(char *str); +bool points_per_sublist( char *str, int32 *npoints, int32 max_lists); +char *scan_to_same_level(char *str); +int objects_inside_point(char *str); +int objects_inside_line(char *str); +int objects_inside_polygon(char *str); +int objects_inside_multipoint(char *str); +int objects_inside_multiline(char *str); +int objects_inside_multipolygon(char *str); +int objects_inside_collection(char *str); +int objects_inside(char *str); +bool parse_objects_inside_point(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +bool parse_objects_inside_multipoint(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +bool parse_objects_inside_line(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +bool parse_objects_inside_multiline(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +bool parse_objects_inside_polygon(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +bool parse_objects_inside_multipolygon(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +bool parse_objects(int32 *obj_size,char **objs,int32 *obj_types,int32 nobjs,char *str, int *offset, bool *is3d); +bool parse_objects_inside_collection(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d); +BOX3D *bbox_of_point(POINT3D *pt); +BOX3D *bbox_of_polygon(POLYGON3D *polygon); +BOX3D *bbox_of_line(LINE3D *line); +BOX3D *union_box3d(BOX3D *a, BOX3D *b); +BOX3D *bbox_of_geometry(GEOMETRY *geom); +GEOMETRY *make_bvol_geometry(BOX3D *box); + bool box3d_ov(BOX3D *box1, BOX3D *box2); +bool is_same_point(POINT3D *p1, POINT3D *p2); +bool is_same_line(LINE3D *l1, LINE3D *l2); +bool is_same_polygon(POLYGON3D *poly1, POLYGON3D *poly2); +BOX *convert_box3d_to_box(BOX3D *in); +double line_length2d(LINE3D *line); +double line_length3d(LINE3D *line); +double polygon_area2d_old(POLYGON3D *poly1); +double polygon_perimeter3d(POLYGON3D *poly1); +double polygon_perimeter2d(POLYGON3D *poly1); +int PIP( POINT3D *P, POINT3D *V, int n ); +bool point_truely_inside(POINT3D *point, BOX3D *box); +int compute_outcode( POINT3D *p, BOX3D *box); +bool lineseg_inside_box( POINT3D *P1, POINT3D *P2, BOX3D *box); +bool linestring_inside_box(POINT3D *pts, int npoints, BOX3D *box); +bool polygon_truely_inside(POLYGON3D *poly, BOX3D *box); +bool line_truely_inside( LINE3D *line, BOX3D *box); +void translate_points(POINT3D *pt, int npoints,double x_off, double y_off, double z_off); +int size_subobject (char *sub_obj, int type); +GEOMETRY *add_to_geometry(GEOMETRY *geom,int sub_obj_size, char *sub_obj, int type); +LINE3D *make_line(int npoints, POINT3D *pts, int *size); +//bool point_within_polygon(POINT3D *, POLYGON3D *); + +void swap_char(char *a, char*b); +void flip_endian_double(char *dd); +void flip_endian_int32(char *ii); + +char *to_wkb(GEOMETRY *geom, bool flip_endian); +char *wkb_multipolygon(POLYGON3D **polys,int numb_polys,int32 *size, bool flipbytes, char byte_order,bool use3d); +char *wkb_polygon(POLYGON3D *poly,int32 *size, bool flipbytes, char byte_order,bool use3d, char *mem); +char *wkb_multiline(LINE3D **lines,int32 *size, int numb_lines, bool flipbytes, char byte_order,bool use3d); +char *wkb_line(LINE3D *line,int32 *size, bool flipbytes, char byte_order,bool use3d, char *mem); +char *wkb_point(POINT3D *pt,int32 *size, bool flipbytes, char byte_order, bool use3d); +char *wkb_multipoint(POINT3D *pt,int32 numb_points,int32 *size, bool flipbytes, char byte_order,bool use3d); + +char *to_wkb_collection(GEOMETRY *geom, bool flip_endian, int32 *size); +char *to_wkb_sub(GEOMETRY *geom, bool flip_endian, int32 *wkb_size); + + + +double deltaLongitude(double azimuth, double sigma, double tsm,SPHEROID *sphere); +double bigA(double u2); +double bigB(double u2); +double distance_ellipse(double lat1, double long1, + double lat2, double long2, + SPHEROID *sphere); +double mu2(double azimuth,SPHEROID *sphere); + +double length2d_ellipse_linestring(LINE3D *line, SPHEROID *sphere); +double length3d_ellipse_linestring(LINE3D *line, SPHEROID *sphere); + +double distance_pt_pt(POINT3D *p1, POINT3D *p2); +double distance_pt_line(POINT3D *p1, LINE3D *l2); +double distance_pt_poly(POINT3D *p1, POLYGON3D *poly2); +double distance_line_line(LINE3D *l1, LINE3D *l2); +double distance_line_poly(LINE3D *l1, POLYGON3D *poly2); +double distance_poly_poly(POLYGON3D *poly1, POLYGON3D *poly2); +double distance_pt_seg(POINT3D *p, POINT3D *A, POINT3D *B); +double distance_seg_seg(POINT3D *A, POINT3D *B, POINT3D *C, POINT3D *D); +bool point_in_poly(POINT3D *p, POLYGON3D *poly); + +POINT3D *segmentize_ring(POINT3D *points, double dist, int num_points_in, int *num_points_out); + + +Datum optimistic_overlap(PG_FUNCTION_ARGS); + + + +unsigned char parse_hex(char *str); +void deparse_hex(unsigned char str, unsigned char *result); + + +char *geometry_to_text(GEOMETRY *geometry); + +BOX3D *parse_box3d(char *str); + +int getint(char *c); +double getdouble(char *c); +GEOMETRY *WKBtoGeometry(char *WKB, int length, int *bytes_read); + +POINT3D *wkb_linearring(char *WKB,char is3d, char flip_endian, int *numbPoints, int *bytes,int bytes_in_stream); + +GEOMETRY *makeNullGeometry(int SRID); + +void compressType(GEOMETRY *g); + +void DP_findsplit(POINT3D *, int, int, int, int *, double *); +void DP_simplify(POINT3D *, int, POINT3D **, int *, double); +char *simplify_line3d(LINE3D *, double); +char *simplify_polygon3d(POLYGON3D *, double); +char *simplify_point3d(POINT3D *, double); + +//exposed to psql + +Datum box3d_in(PG_FUNCTION_ARGS); +Datum box3d_out(PG_FUNCTION_ARGS); +Datum geometry_in(PG_FUNCTION_ARGS); +Datum geometry_out(PG_FUNCTION_ARGS); + +Datum astext_geometry(PG_FUNCTION_ARGS); + +Datum geometry_text(PG_FUNCTION_ARGS); + + +Datum get_bbox_of_geometry(PG_FUNCTION_ARGS); +Datum get_geometry_of_bbox(PG_FUNCTION_ARGS); +Datum box3d_same(PG_FUNCTION_ARGS); +Datum geometry_overleft(PG_FUNCTION_ARGS); +Datum geometry_left(PG_FUNCTION_ARGS); +Datum geometry_right(PG_FUNCTION_ARGS); +Datum geometry_overright(PG_FUNCTION_ARGS); +Datum geometry_contained(PG_FUNCTION_ARGS); +Datum geometry_contain(PG_FUNCTION_ARGS); +Datum geometry_overlap(PG_FUNCTION_ARGS); +Datum geometry_same(PG_FUNCTION_ARGS); +Datum box3d_overlap(PG_FUNCTION_ARGS); +Datum box3d_overleft(PG_FUNCTION_ARGS); +Datum box3d_right(PG_FUNCTION_ARGS); +Datum box3d_contain(PG_FUNCTION_ARGS); +Datum geometry_union(PG_FUNCTION_ARGS); +Datum geometry_inter(PG_FUNCTION_ARGS); +Datum geometry_size(PG_FUNCTION_ARGS); +Datum length3d(PG_FUNCTION_ARGS); +Datum length2d(PG_FUNCTION_ARGS); +Datum area2d(PG_FUNCTION_ARGS); +Datum perimeter3d(PG_FUNCTION_ARGS); +Datum perimeter2d(PG_FUNCTION_ARGS); +Datum truly_inside(PG_FUNCTION_ARGS); + +Datum geometry_lt(PG_FUNCTION_ARGS); +Datum geometry_le(PG_FUNCTION_ARGS); +Datum geometry_eq(PG_FUNCTION_ARGS); +Datum geometry_gt(PG_FUNCTION_ARGS); +Datum geometry_ge(PG_FUNCTION_ARGS); +Datum geometry_cmp(PG_FUNCTION_ARGS); + +Datum npoints(PG_FUNCTION_ARGS); +Datum nrings(PG_FUNCTION_ARGS); +Datum mem_size(PG_FUNCTION_ARGS); +Datum summary(PG_FUNCTION_ARGS); +Datum translate(PG_FUNCTION_ARGS); + +Datum asbinary_specify(PG_FUNCTION_ARGS); +Datum asbinary_simple(PG_FUNCTION_ARGS); + +Datum force_2d(PG_FUNCTION_ARGS); +Datum force_3d(PG_FUNCTION_ARGS); +Datum force_collection(PG_FUNCTION_ARGS); + +Datum combine_bbox(PG_FUNCTION_ARGS); + +Datum dimension(PG_FUNCTION_ARGS); +Datum geometrytype(PG_FUNCTION_ARGS); +Datum envelope(PG_FUNCTION_ARGS); + +Datum x_point(PG_FUNCTION_ARGS); +Datum y_point(PG_FUNCTION_ARGS); +Datum z_point(PG_FUNCTION_ARGS); + +Datum numpoints_linestring(PG_FUNCTION_ARGS); +Datum pointn_linestring(PG_FUNCTION_ARGS); + +Datum exteriorring_polygon(PG_FUNCTION_ARGS); +Datum numinteriorrings_polygon(PG_FUNCTION_ARGS); +Datum interiorringn_polygon(PG_FUNCTION_ARGS); + +Datum numgeometries_collection(PG_FUNCTION_ARGS); +Datum geometryn_collection(PG_FUNCTION_ARGS); + +Datum ellipsoid_out(PG_FUNCTION_ARGS); +Datum ellipsoid_in(PG_FUNCTION_ARGS); +Datum length_ellipsoid(PG_FUNCTION_ARGS); +Datum length3d_ellipsoid(PG_FUNCTION_ARGS); +Datum distance_ellipsoid(PG_FUNCTION_ARGS); +Datum distance_sphere(PG_FUNCTION_ARGS); + +Datum point_inside_circle(PG_FUNCTION_ARGS); +Datum distance(PG_FUNCTION_ARGS); + +Datum expand_bbox(PG_FUNCTION_ARGS); +Datum expand_geometry(PG_FUNCTION_ARGS); +Datum srid_geom(PG_FUNCTION_ARGS); +Datum geometry_from_text(PG_FUNCTION_ARGS); + +Datum startpoint(PG_FUNCTION_ARGS); + +Datum endpoint(PG_FUNCTION_ARGS); +Datum isclosed(PG_FUNCTION_ARGS); + +Datum centroid(PG_FUNCTION_ARGS); + +Datum postgis_gist_sel(PG_FUNCTION_ARGS); + +Datum WKB_in(PG_FUNCTION_ARGS); +Datum WKB_out(PG_FUNCTION_ARGS); + +Datum WKB_recv(PG_FUNCTION_ARGS); + +Datum CHIP_in(PG_FUNCTION_ARGS); +Datum CHIP_out(PG_FUNCTION_ARGS); +Datum CHIP_to_geom(PG_FUNCTION_ARGS); +Datum srid_chip(PG_FUNCTION_ARGS); +Datum setsrid_chip(PG_FUNCTION_ARGS); +Datum width_chip(PG_FUNCTION_ARGS); +Datum height_chip(PG_FUNCTION_ARGS); +Datum datatype_chip(PG_FUNCTION_ARGS); +Datum compression_chip(PG_FUNCTION_ARGS); +Datum setfactor_chip(PG_FUNCTION_ARGS); +Datum factor_chip(PG_FUNCTION_ARGS); + + +Datum segmentize(PG_FUNCTION_ARGS); + +Datum box3d_xmin(PG_FUNCTION_ARGS); +Datum box3d_ymin(PG_FUNCTION_ARGS); +Datum box3d_zmin(PG_FUNCTION_ARGS); + +Datum box3d_xmax(PG_FUNCTION_ARGS); +Datum box3d_ymax(PG_FUNCTION_ARGS); +Datum box3d_zmax(PG_FUNCTION_ARGS); +Datum box3dtobox(PG_FUNCTION_ARGS); + + + +Datum transform_geom(PG_FUNCTION_ARGS); + +Datum max_distance(PG_FUNCTION_ARGS); +Datum geom_accum(PG_FUNCTION_ARGS); +Datum collect_garray(PG_FUNCTION_ARGS); +Datum collector(PG_FUNCTION_ARGS); + +Datum WKBtoBYTEA(PG_FUNCTION_ARGS); + +Datum histogram2d_in(PG_FUNCTION_ARGS); +Datum histogram2d_out(PG_FUNCTION_ARGS); +Datum create_histogram2d(PG_FUNCTION_ARGS); + +Datum build_histogram2d(PG_FUNCTION_ARGS); + +Datum geometry2box(PG_FUNCTION_ARGS); + +Datum explode_histogram2d(PG_FUNCTION_ARGS); +Datum estimate_histogram2d(PG_FUNCTION_ARGS); +#if USE_VERSION >= 80 +Datum geometry_analyze(PG_FUNCTION_ARGS); +#endif + +Datum postgisgistcostestimate(PG_FUNCTION_ARGS); + +Datum geometryfromWKB(PG_FUNCTION_ARGS); +Datum geometryfromWKB_SRID(PG_FUNCTION_ARGS); + +Datum PointfromWKB_SRID(PG_FUNCTION_ARGS); +Datum LinefromWKB_SRID(PG_FUNCTION_ARGS); +Datum PolyfromWKB_SRID(PG_FUNCTION_ARGS); +Datum MPointfromWKB_SRID(PG_FUNCTION_ARGS); +Datum MLinefromWKB_SRID(PG_FUNCTION_ARGS); +Datum MPolyfromWKB_SRID(PG_FUNCTION_ARGS); +Datum GCfromWKB_SRID(PG_FUNCTION_ARGS); + + +Datum geometry_from_text_poly(PG_FUNCTION_ARGS); +Datum geometry_from_text_mpoly(PG_FUNCTION_ARGS); +Datum geometry_from_text_point(PG_FUNCTION_ARGS); +Datum geometry_from_text_mpoint(PG_FUNCTION_ARGS); +Datum geometry_from_text_line(PG_FUNCTION_ARGS); +Datum geometry_from_text_mline(PG_FUNCTION_ARGS); +Datum geometry_from_text_gc(PG_FUNCTION_ARGS); +Datum isempty(PG_FUNCTION_ARGS); +Datum simplify(PG_FUNCTION_ARGS); +Datum line_interpolate_point(PG_FUNCTION_ARGS); + +Datum fluffType(PG_FUNCTION_ARGS); +Datum postgis_uses_stats(PG_FUNCTION_ARGS); +Datum postgis_lib_version(PG_FUNCTION_ARGS); +Datum postgis_geos_version(PG_FUNCTION_ARGS); +Datum postgis_proj_version(PG_FUNCTION_ARGS); + +/*-------------------------------------------------------------------- + * Useful floating point utilities and constants. + * from postgres geo_decls.c + * EPSILON modified to be more "double" friendly + *-------------------------------------------------------------------*/ + + + +// from contrib/cube/cube.c + +#if ! defined(__MINGW32__) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif +#define abs(a) ((a) < (0) ? (-a) : (a)) + + diff --git a/hwgeom/postgis.sql.in b/hwgeom/postgis.sql.in new file mode 100644 index 000000000..c6fb4310c --- /dev/null +++ b/hwgeom/postgis.sql.in @@ -0,0 +1,2801 @@ +-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +-- +-- $Id$ +-- +-- PostGIS - Spatial Types for PostgreSQL +-- http://postgis.refractions.net +-- Copyright 2001-2003 Refractions Research Inc. +-- +-- This is free software; you can redistribute and/or modify it under +-- the terms of the GNU General Public Licence. See the COPYING file. +-- +-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#if USE_VERSION > 71 +#define CREATEFUNCTION CREATE OR REPLACE FUNCTION +#else +#define CREATEFUNCTION CREATE FUNCTION +#endif + +BEGIN TRANSACTION; + +-- You might have to define the PL/PgSQL language usually done with the +-- changelang script. + +-- Here's some hokey code to test to see if PL/PgSQL is installed +-- if it is, you get a message "PL/PgSQL is installed" +-- otherwise it will give a big error message. + +(select 'PL/PgSQL is installed.' as message from pg_language where lanname='plpgsql') union (select 'You must install PL/PgSQL before running this SQL file,\nor you will get an error. To install PL/PgSQL run:\n\tcreatelang plpgsql '::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( , , ) +----------------------------------------------------------------------- +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( ) +----------------------------------------------------------------------- +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_' +-- and 'enforce_srid_' +-- 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( ,
, ) +----------------------------------------------------------------------- +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(
, ) +----------------------------------------------------------------------- +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 ( , ) +----------------------------------------------------------------------- +-- +-- 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 +-- , ,
, , , , +----------------------------------------------------------------------- +-- +-- 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 ( ,
, , , , ) +---------------------------------------------------------------------------- +-- +-- 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 (
, , , , ) +---------------------------------------------------------------------------- +-- +-- 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 +-- , ,
, +----------------------------------------------------------------------- +-- +-- 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 +-- ,
, +----------------------------------------------------------------------- +-- +-- 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 +--
, +----------------------------------------------------------------------- +-- +-- 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 +-- , ,
+----------------------------------------------------------------------- +-- +-- 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 +-- ,
+----------------------------------------------------------------------- +-- +-- 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 +--
+----------------------------------------------------------------------- +-- +-- 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(
, ) +----------------------------------------------------------------------- +-- +-- 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( , ) +----------------------------------------------------------------------- +-- +-- 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( , , ) +----------------------------------------------------------------------- +CREATEFUNCTION build_histogram2d (histogram2d,text,text) + RETURNS histogram2d + AS '@MODULE_FILENAME@','build_histogram2d' + LANGUAGE 'C' with (isstrict); + +#if USE_VERSION >= 73 +----------------------------------------------------------------------- +-- BUILD_HISTOGRAM2D(,,,) +----------------------------------------------------------------------- +-- 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( , ) +----------------------------------------------------------------------- +CREATEFUNCTION explode_histogram2d (HISTOGRAM2D,text) + RETURNS histogram2d + AS '@MODULE_FILENAME@','explode_histogram2d' + LANGUAGE 'C' with (isstrict); + +----------------------------------------------------------------------- +-- ESTIMATE_HISTOGRAM2D( , ) +----------------------------------------------------------------------- +CREATEFUNCTION estimate_histogram2d(HISTOGRAM2D,box) + RETURNS float8 + AS '@MODULE_FILENAME@','estimate_histogram2d' + LANGUAGE 'C' with (isstrict); + + +----------------------------------------------------------------------- +-- SVG OUTPUT +----------------------------------------------------------------------- +CREATEFUNCTION AsSvg(geometry,int4,int4) + RETURNS TEXT + AS '@MODULE_FILENAME@','assvg_geometry' + LANGUAGE 'C'; + +CREATEFUNCTION AsSvg(geometry,int4) + RETURNS TEXT + AS '@MODULE_FILENAME@','assvg_geometry' + LANGUAGE 'C'; + +CREATEFUNCTION AsSvg(geometry) + RETURNS TEXT + AS '@MODULE_FILENAME@','assvg_geometry' + LANGUAGE 'C'; + +END TRANSACTION; diff --git a/hwgeom/postgis_algo.c b/hwgeom/postgis_algo.c new file mode 100644 index 000000000..f0994bc13 --- /dev/null +++ b/hwgeom/postgis_algo.c @@ -0,0 +1,512 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.3 2004/04/27 13:50:59 strk + * Fixed bug in simplify() that was using the square of the given tolerance. + * + * Revision 1.2 2004/01/21 19:04:03 strk + * Added line_interpolate_point function by jsunday@rochgrp.com + * + * Revision 1.1 2003/10/27 10:21:15 strk + * Initial import. + * + **********************************************************************/ + +#include "postgres.h" +#include "postgis.h" + + +/*********************************************************************** + * Simple Douglas-Peucker line simplification. + * No checks are done to avoid introduction of self-intersections. + * No topology relations are considered. + * + * --strk@keybit.net; + ***********************************************************************/ + +#define SAMEPOINT(a,b) ((a)->x==(b)->x&&(a)->y==(b)->y&&(a)->z==(b)->z) +#define VERBOSE 0 + +#if VERBOSE > 0 +#undef REPORT_POINTS_REDUCTION +#undef REPORT_RINGS_REDUCTION +#define REPORT_RINGS_ADJUSTMENTS +#endif + +#undef CHECK_RING_IS_CLOSE + + +/* + * Search farthest point from segment p1-p2 + * returns distance in an int pointer + */ +void DP_findsplit(POINT3D *pts, int npts, int p1, int p2, + int *split, double *dist) +{ + int k; + POINT3D *pa, *pb, *pk; + double tmp; + +#if VERBOSE > 4 +elog(NOTICE, "DP_findsplit called"); +#endif + + *dist = -1; + *split = p1; + + if (p1 + 1 < p2) + { + + pa = &(pts[p1]); + pb = &(pts[p2]); + +#if VERBOSE > 4 +elog(NOTICE, "DP_findsplit: P%d(%f,%f) to P%d(%f,%f)", + p1, pa->x, pa->y, p2, pb->x, pb->y); +#endif + + for (k=p1+1; k 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; rinrings; 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;inobjs; i++) + { + orig_obj = (char *) orig_geom+offsets[i]; + + if ( orig_geom->objType[i] == LINETYPE ) + { + simp_obj = simplify_line3d((LINE3D *)orig_obj, dist); + } + else if ( orig_geom->objType[i] == POLYGONTYPE ) + { + simp_obj = simplify_polygon3d((POLYGON3D *)orig_obj, dist); + } + else if ( orig_geom->objType[i] == POINTTYPE ) + { + simp_obj = simplify_point3d((POINT3D *)orig_obj, dist); + } + else + { + elog(NOTICE, "Unknown geometry type"); + PG_RETURN_NULL(); + } + + /* Simplified object degenerated to empty set */ + if ( ! simp_obj ) continue; + + /* Get size of simplified object */ + simp_obj_size = size_subobject(simp_obj, orig_geom->objType[i]); + + /* Create one-object geometry (will add objects later) */ + if ( simp_geom == NULL ) + { + simp_geom = make_oneobj_geometry( + simp_obj_size, simp_obj, orig_geom->objType[i], + orig_geom->is3d, orig_geom->SRID, orig_geom->scale, + orig_geom->offsetX, orig_geom->offsetY); + } + + /* Add object to already initialized geometry */ + else + { + simp_geom = add_to_geometry( + simp_geom, simp_obj_size, simp_obj, + orig_geom->objType[i] ); + } + + /* Error in simplified geometry construction */ + if ( ! simp_geom ) + { + elog(ERROR, "geometry construction failed at iteration %d", i); + PG_RETURN_NULL(); + } + + } + + if ( simp_geom == NULL ) PG_RETURN_NULL(); + + /* Calculate the bounding box */ + bbox = bbox_of_geometry(simp_geom); + memcpy(&(simp_geom->bvol), bbox, sizeof(BOX3D)); + pfree(bbox); + + + simp_geom->type = orig_geom->type; + PG_RETURN_POINTER(simp_geom); +} +/*********************************************************************** + * --strk@keybit.net; + ***********************************************************************/ + +/*********************************************************************** + * Interpolate a point along a line, useful for Geocoding applications + * SELECT line_interpolate_point( 'LINESTRING( 0 0, 2 2'::geometry, .5 ) + * returns POINT( 1 1 ) + * + * -- jsunday@rochgrp.com; + ***********************************************************************/ +PG_FUNCTION_INFO_V1(line_interpolate_point); +Datum line_interpolate_point(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + double distance = PG_GETARG_FLOAT8(1); + + int32 *offsets1; + LINE3D *line; + POINT3D point; + int nsegs, i; + double length, slength, tlength; + + if( distance < 0 || distance > 1 ) { + elog(ERROR,"line_interpolate_point: 2nd arg isnt within [0,1]"); + PG_RETURN_NULL(); + } + + if( geom->type != LINETYPE ) { + elog(ERROR,"line_interpolate_point: 1st arg isnt a line"); + PG_RETURN_NULL(); + } + + offsets1 = (int32 *) ( ((char *) &(geom->objType[0] )) + + sizeof(int32) * geom->nobjs ); + line = (LINE3D*) ( (char *)geom + offsets1[0] ); + + /* If distance is one of the two extremes, return the point on that + * end rather than doing any expensive computations + */ + if( distance == 0.0 ) { + PG_RETURN_POINTER( + make_oneobj_geometry(sizeof(POINT3D), + /*(char*)&(line->points[0]), POINTTYPE, */ + (char*)&(line->points[0]), POINTTYPE, + geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY + ) + ); + } + + if( distance == 1.0 ) { + PG_RETURN_POINTER( + make_oneobj_geometry(sizeof(POINT3D), + (char*)&(line->points[line->npoints-1]), POINTTYPE, + geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY + ) + ); + } + + /* Interpolate a point on the line */ + nsegs = line->npoints - 1; + length = line_length2d( line ); + tlength = 0; + for( i = 0; i < nsegs; i++ ) { + POINT3D *p1, *p2; + p1 = &(line->points[i]); + p2 = &(line->points[i+1]); + /* Find the relative length of this segment */ + slength = distance_pt_pt( p1, p2 )/length; + /* If our target distance is before the total length we've seen + * so far. create a new point some distance down the current + * segment. + */ + if( distance < tlength + slength ) { + double dseg = (distance - tlength) / slength; + point.x = (p1->x) + ((p2->x - p1->x) * dseg); + point.y = (p1->y) + ((p2->y - p1->y) * dseg); + point.z = 0; + PG_RETURN_POINTER( + make_oneobj_geometry(sizeof(POINT3D), + (char*)&point, POINTTYPE, + geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY + ) + ); + } + tlength += slength; + } + /* Return the last point on the line. This shouldn't happen, but + * could if there's some floating point rounding errors. */ + PG_RETURN_POINTER( + make_oneobj_geometry(sizeof(POINT3D), + (char*)&(line->points[line->npoints-1]), POINTTYPE, + geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY + ) + ); +} +/*********************************************************************** + * --jsunday@rochgrp.com; + ***********************************************************************/ diff --git a/hwgeom/postgis_chip.c b/hwgeom/postgis_chip.c new file mode 100644 index 000000000..4602edb5b --- /dev/null +++ b/hwgeom/postgis_chip.c @@ -0,0 +1,277 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.7 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.6 2003/07/01 18:30:55 pramsey + * Added CVS revision headers. + * + * + **********************************************************************/ + +#include "postgres.h" + + +#include +#include +#include +#include +#include + +#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;tsize = size; + if (result->size < sizeof(CHIP) ) + { + elog(ERROR,"CHIP_in parser - bad data (too small to be a CHIP)!"); + PG_RETURN_NULL(); + } + + + if (result->endian_hint != 1) + { + //need to do an endian flip + flip_endian_int32( (char *) &result->endian_hint); + + flip_endian_double((char *) &result->bvol.LLB.x); + flip_endian_double((char *) &result->bvol.LLB.y); + flip_endian_double((char *) &result->bvol.LLB.z); + + flip_endian_double((char *) &result->bvol.URT.x); + flip_endian_double((char *) &result->bvol.URT.y); + flip_endian_double((char *) &result->bvol.URT.z); + + flip_endian_int32( (char *) & result->SRID); + //dont know what to do with future[8] ... + + flip_endian_int32( (char *) & result->height); + flip_endian_int32( (char *) & result->width); + flip_endian_int32( (char *) & result->compression); + flip_endian_int32( (char *) & result->factor); + flip_endian_int32( (char *) & result->datatype); + + } + if (result->endian_hint != 1 ) + { + elog(ERROR,"CHIP_in parser - bad data (endian flag != 1)!"); + PG_RETURN_NULL(); + } + datum_size = 4; + + if ( (result->datatype == 6) || (result->datatype == 7) || (result->datatype == 106) || (result->datatype == 107) ) + { + datum_size = 2; + } + if ( (result->datatype == 8) || (result->datatype == 108) ) + { + datum_size=1; + } + + if (result->compression ==0) //only true for non-compressed data + { + if (result->size != (sizeof(CHIP) + datum_size * result->width*result->height) ) + { + elog(ERROR,"CHIP_in parser - bad data (actual size != computed size)!"); + PG_RETURN_NULL(); + } + } + + PG_RETURN_POINTER(result); +} + + +//given a CHIP structure, convert it to Hex and put it in a string +PG_FUNCTION_INFO_V1(CHIP_out); +Datum CHIP_out(PG_FUNCTION_ARGS) +{ + CHIP *chip = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + char *result; + int size_result; + int t; + +//printf("chip_out called\n"); + + size_result = (chip->size ) *2 +1; //+1 for null char + result = palloc (size_result); + result[size_result-1] = 0; //null terminate + + for (t=0; t< (chip->size); t++) + { + deparse_hex( ((unsigned char *) chip)[t], &result[t*2]); + } + PG_RETURN_CSTRING(result); +} + + + +PG_FUNCTION_INFO_V1(CHIP_to_geom); +Datum CHIP_to_geom(PG_FUNCTION_ARGS) +{ + CHIP *chip = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GEOMETRY *result; + + POLYGON3D *poly; + POINT3D pts[5]; //5 points around box + int pts_per_ring[1]; + int poly_size; + + //use LLB's z value (we're going to set is3d to false) + + set_point( &pts[0], chip->bvol.LLB.x , chip->bvol.LLB.y , chip->bvol.LLB.z ); + set_point( &pts[1], chip->bvol.URT.x , chip->bvol.LLB.y , chip->bvol.LLB.z ); + set_point( &pts[2], chip->bvol.URT.x , chip->bvol.URT.y , chip->bvol.LLB.z ); + set_point( &pts[3], chip->bvol.LLB.x , chip->bvol.URT.y , chip->bvol.LLB.z ); + set_point( &pts[4], chip->bvol.LLB.x , chip->bvol.LLB.y , chip->bvol.LLB.z ); + + pts_per_ring[0] = 5; //ring has 5 points + + //make a polygon + poly = make_polygon(1, pts_per_ring, pts, 5, &poly_size); + + result = make_oneobj_geometry(poly_size, (char *)poly, POLYGONTYPE, FALSE,chip->SRID, 1.0, 0.0, 0.0); + + PG_RETURN_POINTER(result); + +} + +PG_FUNCTION_INFO_V1(srid_chip); +Datum srid_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + PG_RETURN_INT32(c->SRID); +} + +PG_FUNCTION_INFO_V1(factor_chip); +Datum factor_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + PG_RETURN_FLOAT4(c->factor); +} + + +PG_FUNCTION_INFO_V1(datatype_chip); +Datum datatype_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + PG_RETURN_INT32(c->datatype); +} + +PG_FUNCTION_INFO_V1(compression_chip); +Datum compression_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + PG_RETURN_INT32(c->compression); +} + + +PG_FUNCTION_INFO_V1(height_chip); +Datum height_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + PG_RETURN_INT32(c->height); +} + +PG_FUNCTION_INFO_V1(width_chip); +Datum width_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + PG_RETURN_INT32(c->width); +} + + + +PG_FUNCTION_INFO_V1(setsrid_chip); +Datum setsrid_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + int32 new_srid = PG_GETARG_INT32(1); + CHIP *result; + + result = (CHIP *) palloc(c->size); + + memcpy(result,c,c->size); + result->SRID = new_srid; + + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(setfactor_chip); +Datum setfactor_chip(PG_FUNCTION_ARGS) +{ + CHIP *c = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + float factor = PG_GETARG_FLOAT4(1); + CHIP *result; + + result = (CHIP *) palloc(c->size); + + memcpy(result,c,c->size); + result->factor = factor; + + PG_RETURN_POINTER(result); +} diff --git a/hwgeom/postgis_debug.c b/hwgeom/postgis_debug.c new file mode 100644 index 000000000..2521a1075 --- /dev/null +++ b/hwgeom/postgis_debug.c @@ -0,0 +1,337 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.16 2004/08/20 10:23:19 strk + * removed leak from mem_size() + * + * Revision 1.15 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.14 2004/01/13 20:30:19 strk + * Added useless PG_RETURN_NULL() call to make compiler happy + * + * Revision 1.13 2003/11/19 18:01:31 strk + * CR removed + * + * Revision 1.12 2003/09/16 20:27:12 dblasby + * added ability to delete geometries. + * + * Revision 1.11 2003/08/08 18:19:20 dblasby + * Conformance changes. + * Removed junk from postgis_debug.c and added the first run of the long + * transaction locking support. (this will change - dont use it) + * conformance tests were corrected + * some dos cr/lf removed + * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support) + * pointN(,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 +#include +#include +#include +#include + +#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; inrings;i++) + { + sprintf(tmp," + ring %i has %i points\n",i,poly->npoints[i]); + strcat(result,tmp); + } + } + } + + // create a text obj to return + mytext = (text *) palloc(VARHDRSZ + strlen(result) ); + VARATT_SIZEP(mytext) = VARHDRSZ + strlen(result) ; + memcpy(VARDATA(mytext) , result, strlen(result) ); + pfree(result); + PG_RETURN_POINTER(mytext); +} + + + + + +extern Datum lockcheck(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(lockcheck); + +Datum lockcheck (PG_FUNCTION_ARGS) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + char *colname ; + int id; + HeapTuple rettuple; + TupleDesc tupdesc; + int SPIcode; + char *relname; + bool isnull; + char query[1024]; + int pk_type_oid; + char *pk_id = NULL; + + + elog(NOTICE,"in lockcheck()"); + + /* Make sure trigdata is pointing at what I expect */ + if (!CALLED_AS_TRIGGER(fcinfo)) + elog(ERROR, "lockcheck: not fired by trigger manager"); + + rettuple = trigdata->tg_newtuple; + tupdesc = trigdata->tg_relation->rd_att; + + /* Connect to SPI manager */ + SPIcode = SPI_connect(); + + if (SPIcode != SPI_OK_CONNECT) + { + elog(ERROR,"lockcheck: couldnt open a connection to SPI"); + PG_RETURN_NULL() ; + } + + relname = SPI_getrelname(trigdata->tg_relation); + + colname = trigdata->tg_trigger->tgargs[0]; + + + + + elog(NOTICE,"trigger was executed on the relation: '%s', with attribute named '%s', with locktable named '%s'", relname,colname,"authorization_table"); + + //get the value + + pk_type_oid =SPI_gettypeid ( tupdesc , SPI_fnumber(tupdesc,colname)); + elog(NOTICE,"primary key type # = %i", pk_type_oid ) ; + + if (pk_type_oid == 23) //int4 + { + int id = (int) DatumGetInt32( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) ); + if (isnull) + { + elog(ERROR,"lockcheck - column (%s) of table (%s) is null!",colname,relname); + } + pk_id = palloc(100); + sprintf(pk_id,"%i",id); + } + else if (pk_type_oid == 25) // text + { + Datum id = ( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) ); + if (isnull) + { + elog(ERROR,"lockcheck - column (%s) of table (%s) is null!",colname,relname); + } + pk_id = DatumGetCString(DirectFunctionCall1(textout,id)); + + } + else if (pk_type_oid == 1043) // varchar + { + Datum id = ( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) ); + if (isnull) + { + elog(ERROR,"lockcheck - column (%s) of table (%s) is null!",colname,relname); + } + pk_id = DatumGetCString(DirectFunctionCall1(varcharout,id)); + } + else + { + elog(ERROR,"id column (%s) of table (%s) has to be either int4, text, or varchar. Its - %s (oid %i)",colname,relname,SPI_gettype ( tupdesc , SPI_fnumber(tupdesc,colname) ) ,pk_type_oid ); + } + + + id = (int) DatumGetInt32( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) ); + + sprintf(query,"SELECT authid FROM %s WHERE expires >= now() AND tname = '%s' and fid = '%s'::text", "authorization_table",relname,pk_id); + elog(NOTICE,"about to execute :%s", query); + + SPIcode = SPI_exec(query,0); + if (SPIcode !=SPI_OK_SELECT ) + elog(ERROR,"couldnt execute to test for lock :%s",query); + + + if (SPI_processed >0) + { + // there is a lock - check to see if I have rights to it! + + TupleDesc tupdesc = SPI_tuptable->tupdesc; + SPITupleTable *tuptable = SPI_tuptable; + HeapTuple tuple = tuptable->vals[0]; + char *lockcode = SPI_getvalue(tuple, tupdesc, 1); + + elog(NOTICE,"there is a lock on this row!"); + + // check to see if temp_lock_have_table table exists (it might not exist if they own no locks + sprintf(query,"SELECT * FROM pg_class WHERE relname = 'temp_lock_have_table'"); + SPIcode = SPI_exec(query,0); + if (SPIcode !=SPI_OK_SELECT ) + elog(ERROR,"couldnt execute to test for lockkey temp table :%s",query); + if (SPI_processed ==0) + { + elog(NOTICE,"I do not own any locks (no lock table) - I cannot modify the row"); + //PG_RETURN_NULL(); + SPI_finish(); + //return NULL; + elog(ERROR,"attemted to modify a locked row that I do not have authorization for!"); + } + + + sprintf(query,"SELECT * FROM temp_lock_have_table WHERE xideq(transid , getTransactionID() ) AND lockcode ='%s'",lockcode); + elog(NOTICE,"about to execute :%s", query); + + SPIcode = SPI_exec(query,0); + if (SPIcode !=SPI_OK_SELECT ) + elog(ERROR,"couldnt execute to test for lock aquire:%s",query); + + if (SPI_processed >0) + { + elog(NOTICE,"I own the lock - I can modify the row"); + SPI_finish(); + return PointerGetDatum(rettuple); + } + + elog(NOTICE,"I do not own the lock - I cannot modify the row"); + //PG_RETURN_NULL(); + SPI_finish(); + //return NULL; + elog(ERROR,"attemted to modify a locked row that I do not have authorization for!"); + PG_RETURN_NULL(); + } + else + { + elog(NOTICE,"there is NOT a lock on this row!"); + SPI_finish(); + return PointerGetDatum(rettuple); + } + +} + + +extern Datum getTransactionID(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(getTransactionID); + +Datum getTransactionID(PG_FUNCTION_ARGS) +{ + TransactionId xid = GetCurrentTransactionId(); + PG_RETURN_DATUM( TransactionIdGetDatum(xid) ); +} + + diff --git a/hwgeom/postgis_estimate.c b/hwgeom/postgis_estimate.c new file mode 100644 index 000000000..995067cbb --- /dev/null +++ b/hwgeom/postgis_estimate.c @@ -0,0 +1,1950 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.31 2004/08/19 06:15:58 strk + * USE_VERSION gets 80 where it got 75 + * + * Revision 1.30 2004/08/16 11:03:25 mcayland + * Added DLLIMPORT reference to "default_statistics_target" if we are compiling under Win32. This should make it unnecessary to apply Romi's patch to the PostgreSQL source tree when compiling PostgreSQL ready for PostGIS. + * + * Revision 1.29 2004/06/22 16:52:17 strk + * Standard deviation factor used in histogram extent computation made + * a compile-time define. + * + * Revision 1.28 2004/06/14 07:48:10 strk + * Histogram extent redefinition after hard deviant removal fixed to be + * "at most" the standard deviation based computed. + * + * Revision 1.27 2004/06/11 11:38:57 strk + * Infinite geometries handling. + * Histogram extent re-computation after 'hard deviant' features removal. + * + * Revision 1.26 2004/06/10 18:54:12 strk + * histogram grid size refined to use near-square cells. + * + * Revision 1.25 2004/06/10 15:44:43 strk + * Added standard deviation based histogram extent refinement + * + * Revision 1.24 2004/06/10 13:42:17 strk + * Separated the estimator code in an estimate_selectivity() function. + * Handled complete contaiment and complete miss of histogram by searc box. + * + * Revision 1.23 2004/06/09 09:35:49 strk + * Removed partial pgsql List API copy + * + * Revision 1.22 2004/06/08 17:49:14 strk + * Fixed to build cleanly agains pg75 + * + * Revision 1.21 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.20 2004/03/15 17:07:05 strk + * Added calls to vacuum_delay_point() to give backend a chance of + * interrupting geometry stats computation. + * Set default DEBUG_GEOMETRY_STATS to 0. + * + * Revision 1.19 2004/03/09 00:21:02 strk + * Removed useless code blocks in histogram builder + * + * Revision 1.18 2004/03/09 00:09:56 strk + * estimator applies a gain of AOI/cell_area on each cell it intersects (reverted to previous behaviour) + * + * Revision 1.17 2004/03/04 13:50:45 strk + * postgis_gist_sel(): added warnings if search_box goes outside of histogram grid + * + * Revision 1.16 2004/03/04 09:44:57 strk + * The selectivity estimator does add the full value of each cell it overlaps, + * regardless of the actual overlapping area. Final gain is not applied + * (formerly 1 / minimun between average feature cells occupation and + * search_box cells occupation) + * + * Revision 1.15 2004/03/03 21:59:48 strk + * added check to keep selectivity value in the range of validity (suggested by m.cave) + * + * Revision 1.14 2004/03/01 16:02:41 strk + * histogram's boxesPerSide computed as a function of the column's statistic target + * + * Revision 1.13 2004/02/29 21:53:42 strk + * bug fix in postgis_gist_sel (for PG75): SysCache is not released if not acquired + * + * Revision 1.12 2004/02/26 16:42:59 strk + * Fixed bugs reported by Mark Cave-Ayland . + * 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 +#include +#include +#include +#include + +#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;tboxesPerSide*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;tboxesPerSide*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;yboxesPerSide;y++) + { + for(x=0;xboxesPerSide;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;tboxesPerSide*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;tvals[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;tboxesPerSide*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; ibvol.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; ilow.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; ilow.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 ( geowanl_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;ivalue[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 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; ivalue[i] /= examinedsamples; + +#if DEBUG_GEOMETRY_STATS > 1 + { + int x, y; + for (x=0; xvalue[x+y*cols]); + } + } + } +#endif + + /* + * Write the statistics data + */ + stats->stakind[0] = STATISTIC_KIND_GEOMETRY; + stats->staop[0] = InvalidOid; + stats->stanumbers[0] = (float4 *)geomstats; + stats->numnumbers[0] = geom_stats_size/sizeof(float4); + + stats->stanullfrac = null_cnt/samplerows; + stats->stawidth = total_width/notnull_cnt; + stats->stadistinct = -1.0; + +#if DEBUG_GEOMETRY_STATS + elog(NOTICE, " out: slot 0: kind %d (STATISTIC_KIND_GEOMETRY)", + stats->stakind[0]); + elog(NOTICE, " out: slot 0: op %d (InvalidOid)", stats->staop[0]); + elog(NOTICE, " out: slot 0: numnumbers %d", stats->numnumbers[0]); + elog(NOTICE, " out: null fraction: %d/%d", null_cnt, samplerows); + elog(NOTICE, " out: average width: %d bytes", stats->stawidth); + elog(NOTICE, " out: distinct values: all (no check done)"); +#endif + + stats->stats_valid = true; +} + +/* + * This function will be called when the ANALYZE command is run + * on a column of the "geometry" type. + * + * It will need to return a stats builder function reference + * and a "minimum" sample rows to feed it. + * If we want analisys to be completely skipped we can return + * FALSE and leave output vals untouched. + * + * What we know from this call is: + * + * o The pg_attribute row referring to the specific column. + * Could be used to get reltuples from pg_class (which + * might quite inexact though...) and use them to set an + * appropriate minimum number of sample rows to feed to + * the stats builder. The stats builder will also receive + * a more accurate "estimation" of the number or rows. + * + * o The pg_type row for the specific column. + * Could be used to set stat builder / sample rows + * based on domain type (when postgis will be implemented + * that way). + * + * Being this experimental we'll stick to a static stat_builder/sample_rows + * value for now. + * + */ +PG_FUNCTION_INFO_V1(geometry_analyze); +Datum geometry_analyze(PG_FUNCTION_ARGS) +{ + VacAttrStats *stats = (VacAttrStats *)PG_GETARG_POINTER(0); + Form_pg_attribute attr = stats->attr; + +#if DEBUG_GEOMETRY_STATS + elog(NOTICE, "geometry_analyze called"); +#endif + + /* If the attstattarget column is negative, use the default value */ + /* NB: it is okay to scribble on stats->attr since it's a copy */ + if (attr->attstattarget < 0) + attr->attstattarget = default_statistics_target; + +#if DEBUG_GEOMETRY_STATS + elog(NOTICE, " attribute stat target: %d", attr->attstattarget); +#endif + + /* + * There might be a reason not to analyze this column + * (can we detect the absence of an index?) + */ + //elog(NOTICE, "compute_geometry_stats not implemented yet"); + //PG_RETURN_BOOL(false); + + /* Setup the minimum rows and the algorithm function */ + stats->minrows = 300 * stats->attr->attstattarget; + stats->compute_stats = compute_geometry_stats; + +#if DEBUG_GEOMETRY_STATS + elog(NOTICE, " minrows: %d", stats->minrows); +#endif + + /* Indicate we are done successfully */ + PG_RETURN_BOOL(true); +} + + + +#endif + + diff --git a/hwgeom/postgis_fn.c b/hwgeom/postgis_fn.c new file mode 100644 index 000000000..a449ffbce --- /dev/null +++ b/hwgeom/postgis_fn.c @@ -0,0 +1,3218 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.40 2004/08/26 16:55:09 strk + * max_distance() raises an 'unimplemented yet' error. + * + * Revision 1.39 2004/07/28 16:10:59 strk + * Changed all version functions to return text. + * Renamed postgis_scripts_version() to postgis_scripts_installed() + * Added postgis_scripts_released(). + * Added postgis_full_version(). + * + * Revision 1.38 2004/07/28 13:37:43 strk + * Added postgis_uses_stats and postgis_scripts_version. + * Experimented with PIP short-circuit in within/contains functions. + * Documented new version functions. + * + * Revision 1.37 2004/07/22 16:20:10 strk + * Added postgis_lib_version() and postgis_geos_version() + * + * Revision 1.36 2004/06/03 16:44:56 strk + * Added expand_geometry - expand(geometry, int8) + * + * Revision 1.35 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.34 2004/03/26 00:54:09 dblasby + * added full support for fluffType() + * 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(,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 +#include +#include +#include +#include + +#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(, 0) is the 1st point, and pointN(, 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(, 1) is the 1st point, and pointN(, 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; inpoints;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; inpoints;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; inpoints[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; inpoints[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 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 LLB.y ) + { + Code = 4; + } + } + if (p->x > box->URT.x ) + { + return (Code+2); + } + if (p->x 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; rinrings; 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; inrings;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;isize); + 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; inrings;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;tnobjs;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 + 0x-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;tnpoints;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;tnpoints;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; tnrings;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;tnrings; 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; tnrings;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;tnrings;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; tnrings; 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;tnobjs;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;unrings;u++) + // { + // num_points += poly->npoints[u]; + // } + + num_points_tot += num_points-1; //last point = 1st point + for (v=0;vis3d, 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;tnpoints;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;rnrings;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; iSRID; + + /* 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; onobjs; 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; inobjs; i++) + { + if( i == geom2->nobjs-1 ) + { + size = geom2->size - offsets2[i]; + } + else + { + size = offsets2[i+1] - offsets2[i]; + } + temp = add_to_geometry( result, size, ((char *) geom2 + offsets2[i]), geom2->objType[i] ); + pfree( result ); + result = temp; + } + + result->is3d = geom1->is3d || geom2->is3d; + bbox = bbox_of_geometry( result ); // make bounding box + memcpy( &result->bvol, bbox, sizeof(BOX3D) ); // copy bounding box + pfree( bbox ); // free bounding box + + PG_RETURN_POINTER( result ); +} + +PG_FUNCTION_INFO_V1(box3d_xmin); +Datum box3d_xmin(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + + PG_RETURN_FLOAT8(box1->LLB.x); +} + +PG_FUNCTION_INFO_V1(box3d_ymin); +Datum box3d_ymin(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + + PG_RETURN_FLOAT8(box1->LLB.y); +} + +PG_FUNCTION_INFO_V1(box3d_zmin); +Datum box3d_zmin(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + + PG_RETURN_FLOAT8(box1->LLB.z); +} + +PG_FUNCTION_INFO_V1(box3d_xmax); +Datum box3d_xmax(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + + PG_RETURN_FLOAT8(box1->URT.x); +} + +PG_FUNCTION_INFO_V1(box3d_ymax); +Datum box3d_ymax(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + + PG_RETURN_FLOAT8(box1->URT.y); +} + +PG_FUNCTION_INFO_V1(box3d_zmax); +Datum box3d_zmax(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + + PG_RETURN_FLOAT8(box1->URT.z); +} + +PG_FUNCTION_INFO_V1(box3dtobox); +Datum box3dtobox(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + BOX *out; + out = convert_box3d_to_box(box1); + PG_RETURN_POINTER(out); +} + + +PG_FUNCTION_INFO_V1(isempty); +Datum isempty(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom1= (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + + if (geom1->nobjs ==0) + PG_RETURN_BOOL(TRUE); + PG_RETURN_BOOL(FALSE); +} + + +// converts multi* types with 1 element to the single-element version. +// ie. MULTIPOINT(0 0) --> POINT(0 0) +void compressType(GEOMETRY *g) +{ + if (g->nobjs ==1) + { + if (g->type == MULTIPOINTTYPE) + { + g->type = POINTTYPE; + return; + } + if (g->type == MULTILINETYPE) + { + g->type = LINETYPE; + return; + } + if (g->type == MULTIPOLYGONTYPE) + { + g->type = POLYGONTYPE; + return; + } + + } +} + + +// converts single-type (point,linestring,polygon) +// to multi* types with 1 element +// ie. POINT(0 0) --> MULTIPOINT(0 0) +// +//postgis09=# select fluffType('POINT(0 0)'); +// flufftype +//------------------------- +// SRID=-1;MULTIPOINT(0 0) +//(1 row) +// +//postgis09=# select fluffType('LINESTRING(0 0, 1 1)'); +// flufftype +//------------------------------------ +// SRID=-1;MULTILINESTRING((0 0,1 1)) +//(1 row) + +PG_FUNCTION_INFO_V1(fluffType); +Datum fluffType(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom1= (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GEOMETRY *g; + + g = (GEOMETRY*) palloc( *((int *) geom1) ); + memcpy(g,geom1, *((int *) geom1)); + + if (g->type == POINTTYPE) + { + g->type = MULTIPOINTTYPE; + } + if (g->type == LINETYPE) + { + g->type = MULTILINETYPE; + } + if (g->type == POLYGONTYPE) + { + g->type = MULTIPOLYGONTYPE; + } + + PG_FREE_IF_COPY(geom1,0); + PG_RETURN_POINTER(g); +} + +PG_FUNCTION_INFO_V1(postgis_lib_version); +Datum postgis_lib_version(PG_FUNCTION_ARGS) +{ + char *ver = POSTGIS_LIB_VERSION; + text *result; + result = (text *) palloc(VARHDRSZ + strlen(ver)); + VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ; + memcpy(VARDATA(result), ver, strlen(ver)); + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(postgis_scripts_released); +Datum postgis_scripts_released(PG_FUNCTION_ARGS) +{ + char *ver = POSTGIS_SCRIPTS_VERSION; + text *result; + result = (text *) palloc(VARHDRSZ + strlen(ver)); + VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ; + memcpy(VARDATA(result), ver, strlen(ver)); + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(postgis_uses_stats); +Datum postgis_uses_stats(PG_FUNCTION_ARGS) +{ +#ifdef USE_STATS + PG_RETURN_BOOL(TRUE); +#else + PG_RETURN_BOOL(FALSE); +#endif +} diff --git a/hwgeom/postgis_geos.c b/hwgeom/postgis_geos.c new file mode 100644 index 000000000..1713368f6 --- /dev/null +++ b/hwgeom/postgis_geos.c @@ -0,0 +1,2142 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.39 2004/08/23 15:37:16 strk + * Changed SCRIPTS_VERSION to 0.0.1 + * + * Revision 1.38 2004/07/28 16:10:59 strk + * Changed all version functions to return text. + * Renamed postgis_scripts_version() to postgis_scripts_installed() + * Added postgis_scripts_released(). + * Added postgis_full_version(). + * + * Revision 1.37 2004/07/28 13:37:43 strk + * Added postgis_uses_stats and postgis_scripts_version. + * Experimented with PIP short-circuit in within/contains functions. + * Documented new version functions. + * + * Revision 1.36 2004/07/27 17:51:50 strk + * short-circuit test for 'contains' + * + * Revision 1.35 2004/07/27 17:49:59 strk + * Added short-circuit test for the within function. + * + * Revision 1.34 2004/07/22 16:58:08 strk + * Updated to reflect geos version string split. + * + * Revision 1.33 2004/07/22 16:20:10 strk + * Added postgis_lib_version() and postgis_geos_version() + * + * Revision 1.32 2004/07/01 17:02:05 strk + * Updated to support latest GEOS (actually removed all geos-version related + * switches). + * Fixed an access to unallocated memory. + * + * Revision 1.31 2004/06/16 19:59:36 strk + * Changed GEOS_VERSION to POSTGIS_GEOS_VERSION to avoid future clashes + * + * Revision 1.30 2004/06/16 19:37:54 strk + * Added cleanup needed for GEOS > 1.0 + * + * Revision 1.29 2004/06/16 18:47:59 strk + * Added code to detect geos version. + * Added appropriate includes in geos connectors. + * + * Revision 1.28 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.27 2004/02/12 10:34:49 strk + * changed USE_GEOS check from ifdef / ifndef to if / if ! + * + * Revision 1.26 2003/12/12 12:03:29 strk + * More debugging output, some code cleanup. + * + * Revision 1.25 2003/12/12 10:28:50 strk + * added GEOSnoop OUTPUT debugging info + * + * Revision 1.24 2003/12/12 10:08:24 strk + * Added GEOSnoop function and some optional debugging output for + * geos<->postgis converter (define DEBUG_CONVERTER at top postgis_geos.c) + * + * Revision 1.23 2003/11/05 18:25:08 strk + * moved #ifdef USE_GEOS below prototypes, added NULL implementation of unite_garray + * + * Revision 1.22 2003/11/05 18:02:41 strk + * renamed unite_finalfunc to unite_garray + * + * Revision 1.21 2003/11/04 19:06:08 strk + * added missing first geom pfree in unite_finalfunc + * + * Revision 1.20 2003/10/29 15:53:10 strk + * geoscentroid() removed. both geos and pgis versions are called 'centroid'. + * only one version will be compiled based on USE_GEOS flag. + * + * Revision 1.19 2003/10/29 13:59:40 strk + * Added geoscentroid function. + * + * Revision 1.18 2003/10/28 15:16:17 strk + * unite_sfunc() from postgis_geos.c renamed to geom_accum() and moved in postgis_fn.c + * + * Revision 1.17 2003/10/28 10:59:55 strk + * handled NULL state array in unite_finalfunc, cleaned up some spurios code + * + * Revision 1.16 2003/10/27 23:44:54 strk + * unite_sfunc made always copy input array in long lived memory context. + * It should now work with safer memory. + * + * Revision 1.15 2003/10/27 20:13:05 strk + * Made GeomUnion release memory soon, Added fastunion support functions + * + * Revision 1.14 2003/10/24 21:52:35 strk + * Modified strcmp-based if-else with switch-case in GEOS2POSTGIS() + * using new GEOSGeometryTypeId() interface. + * + * Revision 1.13 2003/10/24 08:28:49 strk + * Fixed memory leak in GEOSGetCoordinates(), made sure that GEOS2POSTGIS + * free type string even in case of collapsed geoms. Made sure that geomunion + * release memory in case of exception thrown by GEOSUnion. Sooner release + * of palloced memory in PolyFromGeometry (pts_per_ring). + * + * Revision 1.12 2003/10/16 20:16:18 dblasby + * Added NOTICE_HANDLER function. For some reason this didnt get properly + * committed last time. + * + * Revision 1.11 2003/10/14 23:19:19 dblasby + * GEOS2POSTGIS - added protection to pfree(NULL) for multi* geoms + * + * Revision 1.10 2003/10/03 16:45:37 dblasby + * added pointonsurface() as a sub. Some memory management fixes/tests. + * removed a few NOTICEs. + * + * Revision 1.9 2003/09/16 20:27:12 dblasby + * added ability to delete geometries. + * + * Revision 1.8 2003/08/08 18:19:20 dblasby + * Conformance changes. + * Removed junk from postgis_debug.c and added the first run of the long + * transaction locking support. (this will change - dont use it) + * conformance tests were corrected + * some dos cr/lf removed + * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support) + * pointN(,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(,[]). + * + * Added all the functions like PolyFromText(,[]) + * + * 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 +#include +#include +#include +#include + +#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; iis3d || 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;tpoints[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;ttype = MULTIPOLYGONTYPE; + return result; + } + for (t=0;tbvol, 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;tsize); +#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;tbvol, 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;tnobjs;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;tnobjs;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;tnobjs;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;tnobjs;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 +#include +#include +#include +#include + +#include "fmgr.h" + + +Datum relate_full(PG_FUNCTION_ARGS); +Datum relate_pattern(PG_FUNCTION_ARGS); +Datum disjoint(PG_FUNCTION_ARGS); +Datum touches(PG_FUNCTION_ARGS); +Datum intersects(PG_FUNCTION_ARGS); +Datum crosses(PG_FUNCTION_ARGS); +Datum within(PG_FUNCTION_ARGS); +Datum contains(PG_FUNCTION_ARGS); +Datum overlaps(PG_FUNCTION_ARGS); +Datum isvalid(PG_FUNCTION_ARGS); + + +Datum buffer(PG_FUNCTION_ARGS); +Datum intersection(PG_FUNCTION_ARGS); +Datum convexhull(PG_FUNCTION_ARGS); +Datum difference(PG_FUNCTION_ARGS); +Datum boundary(PG_FUNCTION_ARGS); +Datum symdifference(PG_FUNCTION_ARGS); +Datum geomunion(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(intersection); +Datum intersection(PG_FUNCTION_ARGS) +{ + elog(ERROR,"intersection:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(convexhull); +Datum convexhull(PG_FUNCTION_ARGS) +{ + elog(ERROR,"convexhull:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(difference); +Datum difference(PG_FUNCTION_ARGS) +{ + elog(ERROR,"difference:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(boundary); +Datum boundary(PG_FUNCTION_ARGS) +{ + elog(ERROR,"boundary:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(symdifference); +Datum symdifference(PG_FUNCTION_ARGS) +{ + elog(ERROR,"symdifference:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(geomunion); +Datum geomunion(PG_FUNCTION_ARGS) +{ + elog(ERROR,"geomunion:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} + +PG_FUNCTION_INFO_V1(buffer); +Datum buffer(PG_FUNCTION_ARGS) +{ + elog(ERROR,"buffer:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} + + + +PG_FUNCTION_INFO_V1(relate_full); +Datum relate_full(PG_FUNCTION_ARGS) +{ + elog(ERROR,"relate_full:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(relate_pattern); +Datum relate_pattern(PG_FUNCTION_ARGS) +{ + elog(ERROR,"relate_pattern:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(disjoint); +Datum disjoint(PG_FUNCTION_ARGS) +{ + elog(ERROR,"disjoint:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(intersects); +Datum intersects(PG_FUNCTION_ARGS) +{ + elog(ERROR,"intersects:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(touches); +Datum touches(PG_FUNCTION_ARGS) +{ + elog(ERROR,"touches:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(crosses); +Datum crosses(PG_FUNCTION_ARGS) +{ + elog(ERROR,"crosses:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(within); +Datum within(PG_FUNCTION_ARGS) +{ + elog(ERROR,"within:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(contains); +Datum contains(PG_FUNCTION_ARGS) +{ + elog(ERROR,"contains:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(overlaps); +Datum overlaps(PG_FUNCTION_ARGS) +{ + elog(ERROR,"overlaps:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(isvalid); +Datum isvalid(PG_FUNCTION_ARGS) +{ + elog(ERROR,"isvalid:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} + +PG_FUNCTION_INFO_V1(issimple); +Datum issimple(PG_FUNCTION_ARGS) +{ + elog(ERROR,"issimple:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(geomequals); +Datum geomequals(PG_FUNCTION_ARGS) +{ + elog(ERROR,"geomequals:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} +PG_FUNCTION_INFO_V1(isring); +Datum isring(PG_FUNCTION_ARGS) +{ + elog(ERROR,"isring:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} + +PG_FUNCTION_INFO_V1(pointonsurface); +Datum pointonsurface(PG_FUNCTION_ARGS) +{ + elog(ERROR,"pointonsurface:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} + +PG_FUNCTION_INFO_V1(unite_garray); +Datum unite_garray(PG_FUNCTION_ARGS) +{ + elog(ERROR,"unite_garray:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); // never get here +} + +PG_FUNCTION_INFO_V1(GEOSnoop); +Datum GEOSnoop(PG_FUNCTION_ARGS) +{ + elog(ERROR,"GEOSnoop:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); +} + +PG_FUNCTION_INFO_V1(postgis_geos_version); +Datum postgis_geos_version(PG_FUNCTION_ARGS) +{ + //elog(ERROR,"GEOSversion:: operation not implemented - compile PostGIS with GEOS support"); + PG_RETURN_NULL(); +} + +#endif + diff --git a/hwgeom/postgis_geos_wrapper.cpp b/hwgeom/postgis_geos_wrapper.cpp new file mode 100644 index 000000000..49e35584e --- /dev/null +++ b/hwgeom/postgis_geos_wrapper.cpp @@ -0,0 +1,1551 @@ +// g++ postgis_GEOSwrapper.cpp -c -I/usr/local/include -I/usr/local/include/geos -I/usr/local/src/postgresql-7.2.3//src/include + +/* +* $Log$ +* Revision 1.1 2004/09/20 07:50:06 strk +* prepared to contain old internal representation code +* +* Revision 1.27 2004/07/22 16:58:08 strk +* Updated to reflect geos version string split. +* +* Revision 1.26 2004/07/22 16:20:10 strk +* Added postgis_lib_version() and postgis_geos_version() +* +* Revision 1.25 2004/07/17 09:52:48 strk +* GEOS multi-version support switches implemented with GEOS_LAST_INTERFACE +* +* Revision 1.24 2004/07/08 19:33:51 strk +* Updated to respect CoordinateSequence GEOS interface switch. +* +* Revision 1.23 2004/07/02 13:33:23 strk +* Changed GEOS header inclusion mechanism to be more polite +* +* Revision 1.22 2004/07/01 17:02:26 strk +* Updated to support latest GEOS API. +* +* Revision 1.21 2004/06/16 19:59:36 strk +* Changed GEOS_VERSION to POSTGIS_GEOS_VERSION to avoid future clashes +* +* Revision 1.20 2004/06/16 19:37:54 strk +* Added cleanup needed for GEOS > 1.0 +* +* Revision 1.19 2004/06/16 18:47:59 strk +* Added code to detect geos version. +* Added appropriate includes in geos connectors. +* +* Revision 1.18 2004/04/27 07:44:26 strk +* Removed use of geometryFactory->toGeometry(), indicated by Martin Davis +* as being intended for internal use only. Created a linear ring instead +* (the function converts a box3d to a geos geometry). +* +* Revision 1.17 2003/12/12 13:34:20 strk +* added missing 'const' in prototypes +* +* Revision 1.16 2003/12/12 12:03:30 strk +* More debugging output, some code cleanup. +* +* Revision 1.15 2003/11/12 16:36:04 strk +* delete all caught exceptions after use +* +* Revision 1.14 2003/10/29 13:58:28 strk +* Added GEOSGetCentroid() function +* +* Revision 1.13 2003/10/24 21:33:21 strk +* Added GEOSGeometryTypeId(Geometry *) wrapper function. +* Added GEOSGetCoordinates_Polygon(Polygon *) as an experimental optimized +* version of GEOSGetCoordinates(Geometry *); More to add ... +* +* Revision 1.12 2003/10/24 14:29:53 strk +* GEOSGetCoordinates() reverted to getCoordinates() call so to be compatible +* to all type of geometries (not only LineStrings) +* +* Revision 1.11 2003/10/24 08:28:50 strk +* Fixed memory leak in GEOSGetCoordinates(), made sure that GEOS2POSTGIS +* free type string even in case of collapsed geoms. Made sure that geomunion +* release memory in case of exception thrown by GEOSUnion. Sooner release +* of palloced memory in PolyFromGeometry (pts_per_ring). +* +* Revision 1.10 2003/10/20 19:50:49 strk +* Removed some memory leaks in PostGIS2* converters. +* +*/ + + +#include + +#include +#include +#include + +#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 *subGeos=new vector; + + 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;tnpoints;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;tnpoints;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 *subPolys=NULL; + Geometry *g; + + subPolys=new vector; + + 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 *subLines = new vector; + 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 *subPoints =new vector; + 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 *innerRings=new vector; + + + pts = (POINT3D *) ( (char *)&(polygon->npoints[polygon->nrings] ) ); + pts = (POINT3D *) MAXALIGN(pts); + + // make outerRing + cl = new DefaultCoordinateSequence(polygon->npoints[0]); + if (is3d) + { + for(t=0;tnpoints[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;tnpoints[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;tnpoints[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;tnpoints[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;tgetAt(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; tgetAt(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; rgetInteriorRingN(r); + cl = lr->getCoordinatesRO(); + npts = lr->getNumPoints(); + for (t=0; tgetAt(t); + result[outidx].x = c.x; + result[outidx].y = c.y; + result[outidx].z = c.z; + outidx++; + } + } + + return result; + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return NULL; + } + + catch(...) + { + return NULL; + } + +} + + + + +int GEOSGetNumCoordinate(Geometry *g1) +{ + try{ + return g1->getNumPoints(); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return 0; + } + + catch(...) + { + return 0; + } +} + +int GEOSGetNumInteriorRings(Geometry *g1) +{ + try{ + Polygon *p = (Polygon *) g1; + return p->getNumInteriorRing(); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return 0; + } + + catch(...) + { + return 0; + } +} + + +//only call on GCs (or multi*) +int GEOSGetNumGeometries(Geometry *g1) +{ + try{ + GeometryCollection *gc = (GeometryCollection *) g1; + return gc->getNumGeometries(); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return 0; + } + + catch(...) + { + return 0; + } +} + + +//call only on GEOMETRYCOLLECTION or MULTI* +const Geometry *GEOSGetGeometryN(Geometry *g1, int n) +{ + try{ + const GeometryCollection *gc = (GeometryCollection *) g1; + return gc->getGeometryN(n); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return NULL; + } + + catch(...) + { + return NULL; + } +} + + +//call only on polygon +const Geometry *GEOSGetExteriorRing(Geometry *g1) +{ + try{ + Polygon *p = (Polygon *) g1; + return p->getExteriorRing(); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return 0; + } + + catch(...) + { + return 0; + } +} + +//call only on polygon +const Geometry *GEOSGetInteriorRingN(Geometry *g1,int n) +{ + try{ + Polygon *p = (Polygon *) g1; + return p->getInteriorRingN(n); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return NULL; + } + + catch(...) + { + return NULL; + } +} + +Geometry *GEOSGetCentroid(Geometry *g) +{ + try{ + return g->getCentroid(); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return NULL; + } + + catch(...) + { + return NULL; + } +} + + +int GEOSGetSRID(Geometry *g1) +{ + try{ + return g1->getSRID(); + } + catch (GEOSException *ge) + { + NOTICE_MESSAGE((char *)ge->toString().c_str()); + delete ge; + return 0; + } + + catch(...) + { + return 0; + } +} + +char * +GEOSversion() +{ +#if GEOS_LAST_INTERFACE < 2 + /* + * GEOS upgrade needs postgis re-build, so this static + * assignment is not going to be a problem + */ + char *res = strdup("1.0.0"); +#else + string version = geosversion(); + char *res = strdup(version.c_str()); +#endif + return res; +} + +char * +GEOSjtsport() +{ +#if GEOS_LAST_INTERFACE < 2 + /* + * GEOS upgrade needs postgis re-build, so this static + * assignment is not going to be a problem + */ + char *res = strdup("1.3"); +#else + string version = jtsport(); + char *res = strdup(version.c_str()); +#endif + return res; +} + + diff --git a/hwgeom/postgis_gist_71.c b/hwgeom/postgis_gist_71.c new file mode 100644 index 000000000..b2391b23f --- /dev/null +++ b/hwgeom/postgis_gist_71.c @@ -0,0 +1,599 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.4 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.3 2003/08/08 18:19:20 dblasby + * Conformance changes. + * Removed junk from postgis_debug.c and added the first run of the long + * transaction locking support. (this will change - dont use it) + * conformance tests were corrected + * some dos cr/lf removed + * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support) + * pointN(,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 +#include +#include +#include +#include + +#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 // FOR ENDIAN DEFINES +#endif + +#define SHOW_DIGS_DOUBLE 15 +#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1) + +// #define DEBUG_GIST + + +//for GIST index +typedef char* (*BINARY_UNION)(char*, char*, int*); +typedef float (*SIZE_BOX)(char*); +typedef Datum (*RDF)(PG_FUNCTION_ARGS); + +GISTENTRY *ggeometry_compress(PG_FUNCTION_ARGS); +GEOMETRYKEY *ggeometry_union(PG_FUNCTION_ARGS); +GIST_SPLITVEC * ggeometry_picksplit(PG_FUNCTION_ARGS); +bool ggeometry_consistent(PG_FUNCTION_ARGS); +float * ggeometry_penalty(PG_FUNCTION_ARGS); +bool * ggeometry_same(PG_FUNCTION_ARGS); + +char * ggeometry_binary_union(char *r1, char *r2, int *sizep); +float size_geometrykey( char *pk ); + +Datum ggeometry_inter(PG_FUNCTION_ARGS); + +/* +** Common rtree-function (for all ops) +*/ +char * rtree_union(bytea *entryvec, int *sizep, BINARY_UNION bu); +float * rtree_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, BINARY_UNION bu, SIZE_BOX sb); +GIST_SPLITVEC * rtree_picksplit(bytea *entryvec, GIST_SPLITVEC *v, int keylen, BINARY_UNION bu, RDF interop, SIZE_BOX sb); +bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); + + +GISTENTRY *rtree_decompress(PG_FUNCTION_ARGS); + + +//restriction in the GiST && operator + +Datum postgis_gist_sel(PG_FUNCTION_ARGS) +{ + PG_RETURN_FLOAT8(0.000005); +} + + +BOX *convert_box3d_to_box(BOX3D *in) +{ + BOX *out = palloc (sizeof (BOX) ); + + out->high.x = in->URT.x; + out->high.y = in->URT.y; + + out->low.x = in->LLB.x; + out->low.y = in->LLB.y; + + return out; +} + +PG_FUNCTION_INFO_V1(ggeometry_compress); +GISTENTRY *ggeometry_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0); + GISTENTRY *retval; + + if ( entry->leafkey) { + retval = palloc(sizeof(GISTENTRY)); + if ( entry->pred ) { + + GEOMETRY *in; + GEOMETRYKEY *r; + BOX *thebox; + +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_compress called on geometry\n"); +#endif + + in = (GEOMETRY*)PG_DETOAST_DATUM(PointerGetDatum(entry->pred)); + + if (in->nobjs ==0) // this is the EMPTY geometry + { + //elog(NOTICE,"found an empty geometry"); + // dont bother adding this to the index + PG_RETURN_POINTER(entry); + } + + r = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) ); + r->size = sizeof(GEOMETRYKEY); + r->SRID = in->SRID; + thebox = convert_box3d_to_box(&in->bvol); + memcpy( (void*)&(r->key), (void*)thebox, sizeof(BOX) ); + if ( (char*)in != entry->pred ) + { + pfree( in ); + pfree(thebox); + } + + gistentryinit(*retval, (char*)r, entry->rel, entry->page, + entry->offset, sizeof(GEOMETRYKEY),FALSE); + + } else { + gistentryinit(*retval, NULL, entry->rel, entry->page, + entry->offset, 0,FALSE); + } + } else { + retval = entry; + } + return( retval ); +} + +PG_FUNCTION_INFO_V1(ggeometry_consistent); +bool ggeometry_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); + GEOMETRY *query = (GEOMETRY*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + BOX *thebox; + /* + ** if entry is not leaf, use gbox_internal_consistent, + ** else use gbox_leaf_consistent + */ + +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_consistent called\n"); +#endif + + if ( ! (entry->pred && query) ) + return FALSE; + + thebox = convert_box3d_to_box( &(query->bvol) ); + + if( ((GEOMETRYKEY *)(entry->pred))->SRID != query->SRID) + { + elog(ERROR,"Operation on two GEOMETRIES with different SRIDs (ggeometry_consistent)\n"); + PG_RETURN_BOOL(FALSE); + } + + PG_RETURN_BOOL(rtree_internal_consistent((BOX*)&( ((GEOMETRYKEY *)(entry->pred))->key ), + thebox, strategy)); +} + + +PG_FUNCTION_INFO_V1(ggeometry_union); +GEOMETRYKEY *ggeometry_union(PG_FUNCTION_ARGS) +{ + GEOMETRYKEY *result; + +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_union called\n"); +#endif + + result = (GEOMETRYKEY*) + rtree_union( + (bytea*) PG_GETARG_POINTER(0), + (int*) PG_GETARG_POINTER(1), + ggeometry_binary_union + ); + + return result; +} + + +PG_FUNCTION_INFO_V1(ggeometry_penalty); +float *ggeometry_penalty(PG_FUNCTION_ARGS) +{ +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_penalty called\n"); +#endif + + return rtree_penalty( + (GISTENTRY*) PG_GETARG_POINTER(0), + (GISTENTRY*) PG_GETARG_POINTER(1), + (float*) PG_GETARG_POINTER(2), + ggeometry_binary_union, + size_geometrykey + ); +} + +PG_FUNCTION_INFO_V1(ggeometry_picksplit); +GIST_SPLITVEC *ggeometry_picksplit(PG_FUNCTION_ARGS) +{ +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_picksplit called\n"); +#endif + + + return rtree_picksplit( + (bytea*)PG_GETARG_POINTER(0), + (GIST_SPLITVEC*)PG_GETARG_POINTER(1), + sizeof(GEOMETRYKEY), + ggeometry_binary_union, + ggeometry_inter, + size_geometrykey + ); +} + +PG_FUNCTION_INFO_V1(ggeometry_same); +bool *ggeometry_same(PG_FUNCTION_ARGS) +{ + + GEOMETRYKEY *b1 = (GEOMETRYKEY*) PG_GETARG_POINTER(0); + GEOMETRYKEY *b2 = (GEOMETRYKEY*) PG_GETARG_POINTER(1); + + bool *result = (bool*) PG_GETARG_POINTER(2); + +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_same called\n"); +#endif + + + if ( b1 && b2 ) + *result = DatumGetBool( DirectFunctionCall2( box_same, + PointerGetDatum(&(b1->key)), + PointerGetDatum(&(b2->key))) ); + else + *result = ( b1==NULL && b2==NULL ) ? TRUE : FALSE; + return(result); +} + +PG_FUNCTION_INFO_V1(ggeometry_inter); +Datum ggeometry_inter(PG_FUNCTION_ARGS) { + GEOMETRYKEY *b1 = (GEOMETRYKEY*) PG_GETARG_POINTER(0); + GEOMETRYKEY*b2 = (GEOMETRYKEY*) PG_GETARG_POINTER(1); + char *interd; + +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_inter called\n"); +#endif + + + interd = DatumGetPointer(DirectFunctionCall2( + rt_box_inter, + PointerGetDatum( &(b1->key) ), + PointerGetDatum( &(b2->key) )) ); + + if (interd) { + GEOMETRYKEY *tmp = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) ); + tmp->size = sizeof(GEOMETRYKEY); + + memcpy( (void*)&(tmp->key), (void*)interd, sizeof(BOX) ); + tmp->SRID = b1->SRID; + pfree( interd ); + PG_RETURN_POINTER( tmp ); + } else + PG_RETURN_POINTER( NULL ); +} + +char *ggeometry_binary_union(char *r1, char *r2, int *sizep) +{ + GEOMETRYKEY *retval; + +#ifdef DEBUG_GIST2 + printf("GIST: ggeometry_binary_union called\n"); +#endif + + if ( ! (r1 && r2) ) { + if ( r1 ) { + retval = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) ); + memcpy( (void*)retval, (void*)r1, sizeof(GEOMETRYKEY) ); + *sizep = sizeof(GEOMETRYKEY); + } else if ( r2 ) { + retval = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) ); + memcpy( (void*)retval, (void*)r2, sizeof(GEOMETRYKEY) ); + *sizep = sizeof(GEOMETRYKEY); + } else { + *sizep = 0; + retval = NULL; + } + } else { + BOX *key = (BOX*)DatumGetPointer( DirectFunctionCall2( + rt_box_union, + PointerGetDatum( &(((GEOMETRYKEY*)r1)->key) ), + PointerGetDatum( &(((GEOMETRYKEY*)r2)->key) )) ); + retval = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) ); + retval->SRID = ((GEOMETRYKEY *) r1)->SRID; + memcpy( (void*)&(retval->key), (void*)key, sizeof(BOX) ); + pfree( key ); + *sizep = retval->size = sizeof(GEOMETRYKEY); + } + return (char*)retval; +} + + +float size_geometrykey( char *pk ) { + +#ifdef DEBUG_GIST2 + printf("GIST: size_geometrykey called\n"); +#endif + + if ( pk ) { + float size; + DirectFunctionCall2( rt_box_size, + PointerGetDatum( &(((GEOMETRYKEY*)pk)->key) ), + PointerGetDatum( &size ) ); + return size; + } else + return 0.0; +} + +char *rtree_union(bytea *entryvec, int *sizep, BINARY_UNION bu) +{ + int numranges, i; + char *out, *tmp; + +#ifdef DEBUG_GIST2 + printf("GIST: rtree_union called\n"); +#endif + + numranges = (VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY); + tmp = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[0]).pred; + out = NULL; + + for (i = 1; i < numranges; i++) { + out = (*bu)(tmp, (char *) + (((GISTENTRY *)(VARDATA(entryvec)))[i]).pred, + sizep); + if (i > 1 && tmp) pfree(tmp); + tmp = out; + } + + return(out); +} + +float *rtree_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, BINARY_UNION bu, SIZE_BOX sb) +{ + char * ud; + float tmp1; + int sizep; + +#ifdef DEBUG_GIST2 + printf("GIST: rtree_penalty called\n"); +#endif + + + + ud = (*bu)( origentry->pred, newentry->pred, &sizep ); + tmp1 = (*sb)( ud ); + if (ud) pfree(ud); + + *result = tmp1 - (*sb)( origentry->pred ); + return(result); +} + +/* +** The GiST PickSplit method +** We use Guttman's poly time split algorithm +*/ +GIST_SPLITVEC *rtree_picksplit(bytea *entryvec, GIST_SPLITVEC *v, int keylen, BINARY_UNION bu, RDF interop, SIZE_BOX sb) +{ + OffsetNumber i, j; + char *datum_alpha, *datum_beta; + char *datum_l, *datum_r; + char *union_d, *union_dl, *union_dr; + char *inter_d; + bool firsttime; + float size_alpha, size_beta, size_union, size_inter; + float size_waste, waste; + float size_l, size_r; + int nbytes; + int sizep; + OffsetNumber seed_1 = 0, seed_2 = 0; + OffsetNumber *left, *right; + OffsetNumber maxoff; + +#ifdef DEBUG_GIST2 + printf("GIST: rtree_picsplit called\n"); +#endif + + maxoff = ((VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY)) - 2; + nbytes = (maxoff + 2) * sizeof(OffsetNumber); + v->spl_left = (OffsetNumber *) palloc(nbytes); + v->spl_right = (OffsetNumber *) palloc(nbytes); + + firsttime = true; + waste = 0.0; + + for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) { + datum_alpha = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[i].pred); + for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) { + datum_beta = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[j].pred); + + /* compute the wasted space by unioning these guys */ + /* size_waste = size_union - size_inter; */ + union_d = (*bu)( datum_alpha, datum_beta, &sizep ); + if ( union_d ) { + size_union = (*sb)(union_d); + pfree(union_d); + } else + size_union = 0.0; + + if ( datum_alpha && datum_beta ) { + inter_d = DatumGetPointer(DirectFunctionCall2( + interop, + PointerGetDatum( datum_alpha ), + PointerGetDatum( datum_beta )) ); + if ( inter_d ) { + size_inter = (*sb)(inter_d); + pfree(inter_d); + } else + size_inter = 0.0; + } else + size_inter = 0.0; + + size_waste = size_union - size_inter; + + /* + * are these a more promising split that what we've + * already seen? + */ + + if (size_waste > waste || firsttime) { + waste = size_waste; + seed_1 = i; + seed_2 = j; + firsttime = false; + } + } + } + + left = v->spl_left; + v->spl_nleft = 0; + right = v->spl_right; + v->spl_nright = 0; + + if ( ((GISTENTRY *)(VARDATA(entryvec)))[seed_1].pred ) { + datum_l = (char*) palloc( keylen ); + memcpy( (void*)datum_l, (void*)(((GISTENTRY *)(VARDATA(entryvec)))[seed_1].pred ), keylen ); + } else + datum_l = NULL; + size_l = (*sb)( datum_l ); + if ( ((GISTENTRY *)(VARDATA(entryvec)))[seed_2].pred ) { + datum_r = (char*) palloc( keylen ); + memcpy( (void*)datum_r, (void*)(((GISTENTRY *)(VARDATA(entryvec)))[seed_2].pred ), keylen ); + } else + datum_r = NULL; + size_r = (*sb)( datum_r ); + + /* + * Now split up the regions between the two seeds. An important + * property of this split algorithm is that the split vector v + * has the indices of items to be split in order in its left and + * right vectors. We exploit this property by doing a merge in + * the code that actually splits the page. + * + * For efficiency, we also place the new index tuple in this loop. + * This is handled at the very end, when we have placed all the + * existing tuples and i == maxoff + 1. + */ + + maxoff = OffsetNumberNext(maxoff); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + + /* + * If we've already decided where to place this item, just + * put it on the right list. Otherwise, we need to figure + * out which page needs the least enlargement in order to + * store the item. + */ + + if (i == seed_1) { + *left++ = i; + v->spl_nleft++; + continue; + } else if (i == seed_2) { + *right++ = i; + v->spl_nright++; + continue; + } + + /* okay, which page needs least enlargement? */ + datum_alpha = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[i].pred); + union_dl = (*bu)( datum_l, datum_alpha, &sizep ); + union_dr = (*bu)( datum_r, datum_alpha, &sizep ); + size_alpha = (*sb)( union_dl ); + size_beta = (*sb)( union_dr ); + + /* pick which page to add it to */ + if (size_alpha - size_l < size_beta - size_r) { + pfree(datum_l); + pfree(union_dr); + datum_l = union_dl; + size_l = size_alpha; + *left++ = i; + v->spl_nleft++; + } else { + pfree(datum_r); + pfree(union_dl); + datum_r = union_dr; + size_r = size_alpha; + *right++ = i; + v->spl_nright++; + } + } + *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ + + v->spl_ldatum = datum_l; + v->spl_rdatum = datum_r; + + return( v ); +} + + +bool rtree_internal_consistent(BOX *key, + BOX *query, + StrategyNumber strategy) +{ + bool retval; + +#ifdef DEBUG_GIST2 + printf("GIST: rtree_internal_consist called\n"); +#endif + + switch(strategy) { + case RTLeftStrategyNumber: + case RTOverLeftStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overleft, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTOverlapStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTOverRightStrategyNumber: + case RTRightStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_right, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTSameStrategyNumber: + case RTContainsStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_contain, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTContainedByStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + default: + retval = FALSE; + } + return(retval); +} + +PG_FUNCTION_INFO_V1(rtree_decompress); +GISTENTRY *rtree_decompress(PG_FUNCTION_ARGS) +{ + return((GISTENTRY*)PG_GETARG_POINTER(0)); +} + + diff --git a/hwgeom/postgis_gist_72.c b/hwgeom/postgis_gist_72.c new file mode 100644 index 000000000..1d68bd193 --- /dev/null +++ b/hwgeom/postgis_gist_72.c @@ -0,0 +1,738 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.19 2004/08/19 06:15:58 strk + * USE_VERSION gets 80 where it got 75 + * + * Revision 1.18 2004/06/09 10:19:06 strk + * Moved changes needed for PG75 inside postgis_gist_72.c using #if switches. + * + * Revision 1.17 2004/06/03 08:19:20 strk + * yet another Infinite check used: finite() - which checks for NaN,-Inf,+Inf + * + * Revision 1.16 2004/06/03 08:13:11 strk + * Simplified INFINITY checks by use of isinf() + * + * Revision 1.15 2004/06/03 07:58:11 strk + * Infinite coordinate geoms omitted from index + * + * Revision 1.14 2004/06/02 23:54:09 strk + * Made equality checks the default in picksplit to catch also NaN results (INF geoms) + * + * Revision 1.13 2004/06/02 23:29:08 strk + * reverted Inf handling modification (conceptually bogus) + * + * Revision 1.12 2004/06/02 22:43:54 strk + * handled special case of Inf boxes as GiST keys in picksplit + * + * Revision 1.11 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.10 2004/04/08 17:05:20 dblasby + * Somehow the memory leak changes I made got removed - I've re-added them. + * + * Revision 1.9 2004/04/08 17:00:27 dblasby + * Changed ggeometry_consistent to be aware of NULL queries. Ie. + * select * from
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(,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 +#include +#include +#include +#include + +#include "access/gist.h" +#include "access/itup.h" +#include "access/rtree.h" + +#include "fmgr.h" + +#include "postgis.h" +#include "utils/elog.h" + +#define SHOW_DIGS_DOUBLE 15 +#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1) + +#define DEBUG_GIST + +//for GIST index +typedef char* (*BINARY_UNION)(char*, char*, int*); +typedef float (*SIZE_BOX)(char*); +typedef Datum (*RDF)(PG_FUNCTION_ARGS); + + +BOX *convert_box3d_to_box(BOX3D *in); +Datum ggeometry_compress(PG_FUNCTION_ARGS); +Datum ggeometry_consistent(PG_FUNCTION_ARGS); +static bool rtree_internal_consistent(BOX *key, BOX *query,StrategyNumber strategy); +static bool rtree_leaf_consistent(BOX *key, BOX *query,StrategyNumber strategy); +Datum rtree_decompress(PG_FUNCTION_ARGS); +Datum gbox_union(PG_FUNCTION_ARGS); +Datum gbox_picksplit(PG_FUNCTION_ARGS); +Datum gbox_penalty(PG_FUNCTION_ARGS); +Datum gbox_same(PG_FUNCTION_ARGS); +static float size_box(Datum box); +extern void convert_box3d_to_box_p(BOX3D *in,BOX *out); + + +int debug = 0; + +//puts result in pre-allocated "out" +void convert_box3d_to_box_p(BOX3D *in,BOX *out) +{ + + out->high.x = in->URT.x; + out->high.y = in->URT.y; + + out->low.x = in->LLB.x; + out->low.y = in->LLB.y; +} + + +BOX *convert_box3d_to_box(BOX3D *in) +{ + BOX *out = palloc (sizeof (BOX) ); + + out->high.x = in->URT.x; + out->high.y = in->URT.y; + + out->low.x = in->LLB.x; + out->low.y = in->LLB.y; + + return out; +} + +PG_FUNCTION_INFO_V1(ggeometry_compress); +Datum ggeometry_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0); + GISTENTRY *retval; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: ggeometry_compress called on geometry\n"); + fflush( stdout ); +#endif + + if ( entry->leafkey) + { + retval = palloc(sizeof(GISTENTRY)); + if ( DatumGetPointer(entry->key) != NULL ) + { + + GEOMETRY *in; + BOX *r; + + + in = (GEOMETRY*)PG_DETOAST_DATUM( entry->key ); + + if (in->nobjs ==0) // this is the EMPTY geometry + { + //elog(NOTICE,"found an empty geometry"); + // dont bother adding this to the index + PG_RETURN_POINTER(entry); + } + + if ( ! finite(in->bvol.URT.x) || + ! finite(in->bvol.URT.y) || + ! finite(in->bvol.LLB.x) || + ! finite(in->bvol.LLB.y) ) + { + //elog(NOTICE, "found infinite geometry"); + PG_RETURN_POINTER(entry); + } + + r = convert_box3d_to_box(&in->bvol); + if ( in != (GEOMETRY*)DatumGetPointer(entry->key) ) + { + pfree( in ); + } + + gistentryinit(*retval, PointerGetDatum(r), + entry->rel, entry->page, + entry->offset, sizeof(BOX), + FALSE); + + } + else + { + gistentryinit(*retval, (Datum) 0, entry->rel, + entry->page, entry->offset, 0,FALSE); + } + } + else + { + retval = entry; + } + PG_RETURN_POINTER(retval); +} + +PG_FUNCTION_INFO_V1(ggeometry_consistent); +Datum ggeometry_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); + GEOMETRY *query ; + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + BOX thebox; + bool result; + +elog(NOTICE, "**** ggeometry_consistent call"); + + /* + ** if entry is not leaf, use rtree_internal_consistent, + ** else use rtree_leaf_consistent + */ + + if ( ( (Pointer *) PG_GETARG_DATUM(1) ) == NULL) + { + //elog(NOTICE,"ggeometry_consistent:: got null query!"); + PG_RETURN_BOOL(false); // null query - this is screwy! + } + + query = (GEOMETRY*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + + + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: ggeometry_consistent called\n"); + fflush( stdout ); +#endif + + if ( ! (DatumGetPointer(entry->key) != NULL && query) ) + PG_RETURN_BOOL(FALSE); + + convert_box3d_to_box_p( &(query->bvol) , &thebox); + + if (GIST_LEAF(entry)) + result = rtree_leaf_consistent((BOX *) DatumGetPointer(entry->key), &thebox, strategy ); + else + result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), &thebox, strategy ); + + PG_FREE_IF_COPY(query, 1); + PG_RETURN_BOOL(result); +} + + +static bool +rtree_internal_consistent(BOX *key, + BOX *query, + StrategyNumber strategy) +{ + bool retval; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: rtree_internal_consist called\n"); + fflush( stdout ); +#endif + + switch(strategy) { + case RTLeftStrategyNumber: + case RTOverLeftStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overleft, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTOverlapStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTOverRightStrategyNumber: + case RTRightStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overright, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTSameStrategyNumber: + case RTContainsStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_contain, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTContainedByStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + default: + retval = FALSE; + } + return(retval); +} + + +static bool +rtree_leaf_consistent(BOX *key, + BOX *query, + StrategyNumber strategy) +{ + bool retval; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: rtree_leaf_consist called\n"); + fflush( stdout ); +#endif + + switch (strategy) + { + case RTLeftStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTOverLeftStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTOverlapStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTOverRightStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTRightStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTSameStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTContainsStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTContainedByStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_contained, PointerGetDatum(key), PointerGetDatum(query))); + break; + default: + retval = FALSE; + } + return (retval); +} + + +PG_FUNCTION_INFO_V1(rtree_decompress); +Datum rtree_decompress(PG_FUNCTION_ARGS) +{ + elog(NOTICE, "rtree_decompress called"); + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); +} + +/* +** The GiST Union method for boxes +** returns the minimal bounding box that encloses all the entries in entryvec +*/ +PG_FUNCTION_INFO_V1(gbox_union); +Datum gbox_union(PG_FUNCTION_ARGS) +{ +#if USE_VERSION < 80 + bytea *entryvec = (bytea *) PG_GETARG_POINTER(0); +#else + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); +#endif + int *sizep = (int *) PG_GETARG_POINTER(1); + int numranges, + i; + BOX *cur, + *pageunion; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_union called\n"); + fflush( stdout ); +#endif + +#if USE_VERSION < 80 + numranges = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY); + cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[0].key); +#else + numranges = entryvec->n; + cur = DatumGetBoxP(entryvec->vector[0].key); +#endif + pageunion = (BOX *) palloc(sizeof(BOX)); + memcpy((void *) pageunion, (void *) cur, sizeof(BOX)); + + for (i = 1; i < numranges; i++) + { +#if USE_VERSION < 80 + cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key); +#else + cur = DatumGetBoxP(entryvec->vector[i].key); +#endif + if (pageunion->high.x < cur->high.x) + pageunion->high.x = cur->high.x; + if (pageunion->low.x > cur->low.x) + pageunion->low.x = cur->low.x; + if (pageunion->high.y < cur->high.y) + pageunion->high.y = cur->high.y; + if (pageunion->low.y > cur->low.y) + pageunion->low.y = cur->low.y; + } + *sizep = sizeof(BOX); + + PG_RETURN_POINTER(pageunion); +} + +/* +** The GiST Penalty method for boxes +** As in the R-tree paper, we use change in area as our penalty metric +*/ +PG_FUNCTION_INFO_V1(gbox_penalty); +Datum gbox_penalty(PG_FUNCTION_ARGS) +{ + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *result = (float *) PG_GETARG_POINTER(2); + Datum ud; + float tmp1; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_penalty called\n"); + fflush( stdout ); +#endif + + ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key); + tmp1 = size_box(ud); + if (DatumGetPointer(ud) != NULL) + pfree(DatumGetPointer(ud)); + + *result = tmp1 - size_box(origentry->key); + PG_RETURN_POINTER(result); +} + +typedef struct { + BOX *key; + int pos; +} KBsort; + +static int +compare_KB(const void* a, const void* b) { + BOX *abox = ((KBsort*)a)->key; + BOX *bbox = ((KBsort*)b)->key; + float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y); + float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y); + + if ( sa==sb ) return 0; + return ( sa>sb ) ? 1 : -1; +} + +/* +** The GiST PickSplit method +** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree', +** C.H.Ang and T.C.Tan +*/ +PG_FUNCTION_INFO_V1(gbox_picksplit); +Datum +gbox_picksplit(PG_FUNCTION_ARGS) +{ +#if USE_VERSION < 80 + bytea *entryvec = (bytea *) PG_GETARG_POINTER(0); +#else + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); +#endif + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + OffsetNumber i; + OffsetNumber *listL, + *listR, + *listB, + *listT; + BOX *unionL, + *unionR, + *unionB, + *unionT; + int posL, + posR, + posB, + posT; + BOX pageunion; + BOX *cur; + char direction = ' '; + bool allisequal = true; + OffsetNumber maxoff; + int nbytes; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_picksplit called\n"); + fflush( stdout ); +#endif + + posL = posR = posB = posT = 0; +#if USE_VERSION < 80 + maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 1; + cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[FirstOffsetNumber].key); +#else + maxoff = entryvec->n - 1; + cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key); +#endif + + memcpy((void *) &pageunion, (void *) cur, sizeof(BOX)); + + /* find MBR */ + for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i)) + { +#if USE_VERSION < 80 + cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key); +#else + cur = DatumGetBoxP(entryvec->vector[i].key); +#endif + if ( allisequal == true && ( + pageunion.high.x != cur->high.x || + pageunion.high.y != cur->high.y || + pageunion.low.x != cur->low.x || + pageunion.low.y != cur->low.y + ) ) + allisequal = false; + + if (pageunion.high.x < cur->high.x) + pageunion.high.x = cur->high.x; + if (pageunion.low.x > cur->low.x) + pageunion.low.x = cur->low.x; + if (pageunion.high.y < cur->high.y) + pageunion.high.y = cur->high.y; + if (pageunion.low.y > cur->low.y) + pageunion.low.y = cur->low.y; + } + + nbytes = (maxoff + 2) * sizeof(OffsetNumber); + listL = (OffsetNumber *) palloc(nbytes); + listR = (OffsetNumber *) palloc(nbytes); + unionL = (BOX *) palloc(sizeof(BOX)); + unionR = (BOX *) palloc(sizeof(BOX)); + if (allisequal) + { +#if USE_VERSION < 80 + cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[OffsetNumberNext(FirstOffsetNumber)].key); +#else + cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key); +#endif + if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0) + { + v->spl_left = listL; + v->spl_right = listR; + v->spl_nleft = v->spl_nright = 0; + memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX)); + memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX)); + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + if (i <= (maxoff - FirstOffsetNumber + 1) / 2) + { + v->spl_left[v->spl_nleft] = i; + v->spl_nleft++; + } + else + { + v->spl_right[v->spl_nright] = i; + v->spl_nright++; + } + } + v->spl_ldatum = BoxPGetDatum(unionL); + v->spl_rdatum = BoxPGetDatum(unionR); + + PG_RETURN_POINTER(v); + } + } + + listB = (OffsetNumber *) palloc(nbytes); + listT = (OffsetNumber *) palloc(nbytes); + unionB = (BOX *) palloc(sizeof(BOX)); + unionT = (BOX *) palloc(sizeof(BOX)); + +#define ADDLIST( list, unionD, pos, num ) do { \ + if ( pos ) { \ + if ( unionD->high.x < cur->high.x ) unionD->high.x = cur->high.x; \ + if ( unionD->low.x > cur->low.x ) unionD->low.x = cur->low.x; \ + if ( unionD->high.y < cur->high.y ) unionD->high.y = cur->high.y; \ + if ( unionD->low.y > cur->low.y ) unionD->low.y = cur->low.y; \ + } else { \ + memcpy( (void*)unionD, (void*) cur, sizeof( BOX ) ); \ + } \ + list[pos] = num; \ + (pos)++; \ +} while(0) + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { +#if USE_VERSION < 80 + cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key); +#else + cur = DatumGetBoxP(entryvec->vector[i].key); +#endif + if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) + ADDLIST(listL, unionL, posL,i); + else + ADDLIST(listR, unionR, posR,i); + + if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) + ADDLIST(listB, unionB, posB,i); + else + ADDLIST(listT, unionT, posT,i); + } + + /* bad disposition, sort by ascending and resplit */ + if ( (posR==0 || posL==0) && (posT==0 || posB==0) ) { + KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff ); + posL = posR = posB = posT = 0; + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { +#if USE_VERSION < 80 + arr[i-1].key = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key); +#else + arr[i-1].key = DatumGetBoxP(entryvec->vector[i].key); +#endif + arr[i-1].pos = i; + } + qsort( arr, maxoff, sizeof(KBsort), compare_KB ); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + cur = arr[i-1].key; + if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) + ADDLIST(listL, unionL, posL,arr[i-1].pos); + else if ( cur->low.x - pageunion.low.x > pageunion.high.x - cur->high.x ) + ADDLIST(listR, unionR, posR,arr[i-1].pos); + else + { + if ( posL>posR ) + ADDLIST(listR, unionR, posR,arr[i-1].pos); + else + ADDLIST(listL, unionL, posL,arr[i-1].pos); + } + + if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) + ADDLIST(listB, unionB, posB,arr[i-1].pos); + else if ( cur->low.y - pageunion.low.y > pageunion.high.y - cur->high.y ) + ADDLIST(listT, unionT, posT,arr[i-1].pos); + else + { + if ( posB>posT ) + ADDLIST(listT, unionT, posT,arr[i-1].pos); + else + ADDLIST(listB, unionB, posB,arr[i-1].pos); + } + } + pfree(arr); + } + + /* which split more optimal? */ + if (Max(posL, posR) < Max(posB, posT)) + direction = 'x'; + else if (Max(posL, posR) > Max(posB, posT)) + direction = 'y'; + else + { + Datum interLR = DirectFunctionCall2(rt_box_inter, + BoxPGetDatum(unionL), + BoxPGetDatum(unionR)); + Datum interBT = DirectFunctionCall2(rt_box_inter, + BoxPGetDatum(unionB), + BoxPGetDatum(unionT)); + float sizeLR, + sizeBT; + + sizeLR = size_box(interLR); + sizeBT = size_box(interBT); + + if (sizeLR < sizeBT) + direction = 'x'; + else + direction = 'y'; + } + + if (direction == 'x') + { + pfree(unionB); + pfree(listB); + pfree(unionT); + pfree(listT); + + v->spl_left = listL; + v->spl_right = listR; + v->spl_nleft = posL; + v->spl_nright = posR; + v->spl_ldatum = BoxPGetDatum(unionL); + v->spl_rdatum = BoxPGetDatum(unionR); + } + else + { + pfree(unionR); + pfree(listR); + pfree(unionL); + pfree(listL); + + v->spl_left = listB; + v->spl_right = listT; + v->spl_nleft = posB; + v->spl_nright = posT; + v->spl_ldatum = BoxPGetDatum(unionB); + v->spl_rdatum = BoxPGetDatum(unionT); + } + + PG_RETURN_POINTER(v); +} + +/* +** Equality method +*/ +PG_FUNCTION_INFO_V1(gbox_same); +Datum gbox_same(PG_FUNCTION_ARGS) +{ + BOX *b1 = (BOX *) PG_GETARG_POINTER(0); + BOX *b2 = (BOX *) PG_GETARG_POINTER(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_same called\n"); + fflush( stdout ); +#endif + + if (b1 && b2) + *result = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(b1), PointerGetDatum(b2))); + else + *result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE; + PG_RETURN_POINTER(result); +} + +static float +size_box(Datum box) +{ + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: size_box called\n"); + fflush( stdout ); +#endif + + if (DatumGetPointer(box) != NULL) + { + float size; + + DirectFunctionCall2(rt_box_size, + box, PointerGetDatum(&size)); + return size; + } + else + return 0.0; +} diff --git a/hwgeom/postgis_inout.c b/hwgeom/postgis_inout.c new file mode 100644 index 000000000..d12ccdfa5 --- /dev/null +++ b/hwgeom/postgis_inout.c @@ -0,0 +1,4906 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.45 2004/08/11 17:07:26 strk + * Fixed a bug in non-finite Z check + * + * Revision 1.44 2004/06/09 09:08:53 strk + * changed index/rindex to strchr/strrchr + * + * Revision 1.43 2004/06/08 15:18:12 strk + * Deleted prototype for isspace() in postgis.h + * and included 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={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(,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(,[]). + * + * Added all the functions like PolyFromText(,[]) + * + * 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 . + * + * Revision 1.21 2003/07/01 18:30:55 pramsey + * Added CVS revision headers. + * + * + **********************************************************************/ + +#include "postgres.h" + + +#include +#include +#include +#include +#include +#include + +#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 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 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= 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;tnpoints[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;tLLB.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; inrings; 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; ix < 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; inobjs;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; tsize = 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; tobjType[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;' 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;tnobjs; 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; tnpoints; + 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;tnpoints; 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; tpoints)[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;tnpoints; + } + + + 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;tnpoints[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;tnrings; 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;unpoints[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;tnrings; 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;unpoints[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;tnrings; + for (u=0;unrings; 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;ttype == 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; tnobjs; 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;tnobjs; 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;tnobjs; 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;tnobjs; 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 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;tnrings;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;tnpoints[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;tsize - 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;tvalue[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;tvalue[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;tboxesPerSide*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(, [] ) +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;ttype = 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;ttype = 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;ttype = 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;ttype = COLLECTIONTYPE; + + if (ngeoms ==1) + return so_far; + + for (t=1;ttype != POLYGONTYPE) + { + elog(ERROR,"geometry_from_text_poly:: WKT isnt POLYGON"); + } + PG_RETURN_POINTER(geom); +} + +PG_FUNCTION_INFO_V1(geometry_from_text_line); +Datum geometry_from_text_line(PG_FUNCTION_ARGS) +{ + int SRID; + GEOMETRY *geom; + + if ( ! PG_ARGISNULL(1) ) + SRID = PG_GETARG_INT32(1); + else + SRID = -1; + + + geom = (GEOMETRY *) DatumGetPointer( + DirectFunctionCall2(geometry_from_text, + PG_GETARG_DATUM(0),Int32GetDatum(SRID) + )); + if (geom->type != LINETYPE) + { + elog(ERROR,"geometry_from_text_line:: WKT isnt LINESTRING"); + } + PG_RETURN_POINTER(geom); +} + + +PG_FUNCTION_INFO_V1(geometry_from_text_point); +Datum geometry_from_text_point(PG_FUNCTION_ARGS) +{ + int SRID; + GEOMETRY *geom; + + if ( ! PG_ARGISNULL(1) ) + SRID = PG_GETARG_INT32(1); + else + SRID = -1; + + + geom = (GEOMETRY *) DatumGetPointer( + DirectFunctionCall2(geometry_from_text, + PG_GETARG_DATUM(0),Int32GetDatum(SRID) + )); + if (geom->type != POINTTYPE) + { + elog(ERROR,"geometry_from_text_point:: WKT isnt POINT"); + } + PG_RETURN_POINTER(geom); +} + +PG_FUNCTION_INFO_V1(geometry_from_text_mpoint); +Datum geometry_from_text_mpoint(PG_FUNCTION_ARGS) +{ + int SRID; + GEOMETRY *geom; + + if ( ! PG_ARGISNULL(1) ) + SRID = PG_GETARG_INT32(1); + else + SRID = -1; + + + geom = (GEOMETRY *) DatumGetPointer( + DirectFunctionCall2(geometry_from_text, + PG_GETARG_DATUM(0),Int32GetDatum(SRID) + )); + if (geom->type != MULTIPOINTTYPE) + { + elog(ERROR,"geometry_from_text_mpoint:: WKT isnt MULTIPOINT"); + } + PG_RETURN_POINTER(geom); +} + +PG_FUNCTION_INFO_V1(geometry_from_text_mline); +Datum geometry_from_text_mline(PG_FUNCTION_ARGS) +{ + int SRID; + GEOMETRY *geom; + + if ( ! PG_ARGISNULL(1) ) + SRID = PG_GETARG_INT32(1); + else + SRID = -1; + + + geom = (GEOMETRY *) DatumGetPointer( + DirectFunctionCall2(geometry_from_text, + PG_GETARG_DATUM(0),Int32GetDatum(SRID) + )); + if (geom->type != MULTILINETYPE) + { + elog(ERROR,"geometry_from_text_mline:: WKT isnt MULTILINESTRING"); + } + PG_RETURN_POINTER(geom); +} + +PG_FUNCTION_INFO_V1(geometry_from_text_mpoly); +Datum geometry_from_text_mpoly(PG_FUNCTION_ARGS) +{ + int SRID; + GEOMETRY *geom; + + if ( ! PG_ARGISNULL(1) ) + SRID = PG_GETARG_INT32(1); + else + SRID = -1; + + + geom = (GEOMETRY *) DatumGetPointer( + DirectFunctionCall2(geometry_from_text, + PG_GETARG_DATUM(0),Int32GetDatum(SRID) + )); + if (geom->type != MULTIPOLYGONTYPE) + { + elog(ERROR,"geometry_from_text_mpoly:: WKT isnt MULTIPOLYGON"); + } + PG_RETURN_POINTER(geom); +} + +PG_FUNCTION_INFO_V1(geometry_from_text_gc); +Datum geometry_from_text_gc(PG_FUNCTION_ARGS) +{ + int SRID; + GEOMETRY *geom; + + if ( ! PG_ARGISNULL(1) ) + SRID = PG_GETARG_INT32(1); + else + SRID = -1; + + + geom = (GEOMETRY *) DatumGetPointer( + DirectFunctionCall2(geometry_from_text, + PG_GETARG_DATUM(0),Int32GetDatum(SRID) + )); + if (geom->type != COLLECTIONTYPE) + { + elog(ERROR,"geometry_from_text_gc:: WKT isnt GEOMETRYCOLLECTION"); + } + PG_RETURN_POINTER(geom); +} + + +//returns a GEOMETRYCOLLECTION(EMPTY) +// bbox of this object is BOX3D(0 0 0, 0 0 0) +// but should be treated as NULL +GEOMETRY *makeNullGeometry(int SRID) +{ + int size = sizeof(GEOMETRY); + GEOMETRY *result = palloc(size); + + + memset(result,0, size ); // init to 0s + + result->size = size; + result->nobjs = 0; + result->type = COLLECTIONTYPE; + result->is3d = false; + + + result->SRID = SRID; + result->scale = 1.0; + result->offsetX = 0; + result->offsetY = 0; + + memset(&result->bvol,0, sizeof(BOX3D) ); //make bbox :: BOX3D(0 0 0, 0 0 0) + + return result; +} + + diff --git a/hwgeom/postgis_ops.c b/hwgeom/postgis_ops.c new file mode 100644 index 000000000..188cad32a --- /dev/null +++ b/hwgeom/postgis_ops.c @@ -0,0 +1,1123 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.13 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.12 2003/11/20 15:34:02 strk + * expected in-transaction memory release for btree operators + * + * Revision 1.11 2003/11/19 15:26:57 strk + * Added geometry_le, geometry_ge, geometry_cmp functions, + * modified geometry_lt, geometry_gt, geometry_eq to be consistent. + * + * Revision 1.10 2003/07/25 17:08:37 pramsey + * Moved Cygwin endian define out of source files into postgis.h common + * header file. + * + * Revision 1.9 2003/07/01 18:30:55 pramsey + * Added CVS revision headers. + * + * + **********************************************************************/ + +#include "postgres.h" + +#include +#include +#include +#include +#include + +#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;inpoints; 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;inobjs; i++) //for each object in geom2 + { + o2 = (char *) geom2 +offsets2[i] ; + type2= geom2->objType[j]; + + if ( (type1 == type2) && (!(already_hit[i])) ) + { + //see if they match + if (type1 == POINTTYPE) + { + if (is_same_point((POINT3D *) o1,(POINT3D *) o2)) + { + already_hit[i] = TRUE; + break; + } + } + if (type1 == LINETYPE) + { + if (is_same_line((LINE3D *) o1,(LINE3D *) o2)) + { + already_hit[i] = TRUE; + break; + } + + } + if (type2 == POLYGONTYPE) + { + if (is_same_polygon((POLYGON3D *) o1,(POLYGON3D *) o2)) + { + already_hit[i] = TRUE; + break; + } + } + } + } + if (i == geom1->nobjs) + PG_RETURN_BOOL(FALSE); // couldnt find match + } + PG_RETURN_BOOL(TRUE); +} + +/******************************************************* + *Box3d routines + *******************************************************/ + + + +/* box_overlap - does box1 overlap box2? + */ +PG_FUNCTION_INFO_V1(box3d_overlap); +Datum box3d_overlap(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + BOX3D *box2 = (BOX3D *) PG_GETARG_POINTER(1); + + PG_RETURN_BOOL(box3d_ov(box1, box2)); +} + +/* box_overleft - is the right edge of box1 to the left of + * the right edge of box2? + * + * This is "less than or equal" for the end of a time range, + * when time ranges are stored as rectangles. + */ +PG_FUNCTION_INFO_V1(box3d_overleft); +Datum box3d_overleft(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + BOX3D *box2 = (BOX3D *) PG_GETARG_POINTER(1); + + bool result; + + result = FPle(box1->URT.x, box2->URT.x); + +//printf("box3d_overleft about to return %i\n",(int) result); + + PG_RETURN_BOOL(result); +} + +/* box_right - is box1 strictly right of box2? + */ +PG_FUNCTION_INFO_V1(box3d_right); +Datum box3d_right(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + BOX3D *box2 = (BOX3D *) PG_GETARG_POINTER(1); + + bool result; + + result = FPgt(box1->LLB.x, box2->URT.x); +//printf("box3d_rightabout to return %i\n",(int) result); + + PG_RETURN_BOOL(result); +} + +/* box_contain - does box1 contain box2? + */ +PG_FUNCTION_INFO_V1(box3d_contain); +Datum box3d_contain(PG_FUNCTION_ARGS) +{ + BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0); + BOX3D *box2 = (BOX3D *) PG_GETARG_POINTER(1); + + bool result; + + result = FPge(box1->URT.x, box2->URT.x) && + FPle(box1->LLB.x, box2->LLB.x) && + FPge(box1->URT.y, box2->URT.y) && + FPle(box1->LLB.y, box2->LLB.y); + //printf("box3d_contain about to return %i\n",(int) result); + + + PG_RETURN_BOOL(result); +} + + +/****************************************************** + * RTREE index requires these 3 functions + ******************************************************/ + +PG_FUNCTION_INFO_V1(geometry_union); +Datum geometry_union(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GEOMETRY *geom2 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + GEOMETRY *result = (GEOMETRY *) palloc(sizeof(GEOMETRY) ); + BOX3D *n; + + if (geom1->SRID != geom2->SRID) + { + elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n"); + PG_RETURN_NULL(); + } + + + result->size = sizeof(GEOMETRY); + result->type = BBOXONLYTYPE; + result->nobjs = -1; + result->SRID = geom1->SRID; + result->scale = geom1->scale; + result->offsetX = geom1->offsetX; + result->offsetY = geom1->offsetY; + + + n = &result->bvol; + + //cheat - just change the bbox + + n->URT.x = max(geom1->bvol.URT.x, geom2->bvol.URT.x); + n->URT.y = max(geom1->bvol.URT.y, geom2->bvol.URT.y); + n->LLB.x = min(geom1->bvol.LLB.x, geom2->bvol.LLB.x); + n->LLB.y = min(geom1->bvol.LLB.y, geom2->bvol.LLB.y); + + + n->URT.z = max(geom1->bvol.URT.z, geom2->bvol.URT.z); + n->LLB.z = min(geom1->bvol.LLB.z, geom2->bvol.LLB.z); + + PG_FREE_IF_COPY(geom1, 0); + PG_FREE_IF_COPY(geom2, 1); + + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(geometry_inter); +Datum geometry_inter(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GEOMETRY *geom2 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + GEOMETRY *result = (GEOMETRY *) palloc(sizeof(GEOMETRY) ); + + if (geom1->SRID != geom2->SRID) + { + elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n"); + PG_RETURN_NULL(); + } + + result->size = sizeof(GEOMETRY); + result->type = BBOXONLYTYPE; + result->nobjs = -1; + result->SRID = geom1->SRID; + result->scale = geom1->scale; + result->offsetX = geom1->offsetX; + result->offsetY = geom1->offsetY; + + result->bvol.URT.x = min(geom1->bvol.URT.x, geom2->bvol.URT.x); + result->bvol.URT.y = min(geom1->bvol.URT.y, geom2->bvol.URT.y); + result->bvol.URT.z = min(geom1->bvol.URT.z, geom2->bvol.URT.z); + + result->bvol.LLB.x = max(geom1->bvol.LLB.x, geom2->bvol.LLB.x); + result->bvol.LLB.y = max(geom1->bvol.LLB.y, geom2->bvol.LLB.y); + result->bvol.LLB.z = max(geom1->bvol.LLB.z, geom2->bvol.LLB.z); + + if (result->bvol.URT.x < result->bvol.LLB.x || result->bvol.URT.y < result->bvol.LLB.y) + { + pfree(result); + /* Indicate "no intersection" by returning NULL pointer */ + result = NULL; + } + PG_FREE_IF_COPY(geom1, 0); + PG_FREE_IF_COPY(geom2, 1); + + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(geometry_size); +Datum geometry_size(PG_FUNCTION_ARGS) +{ + Pointer aptr = PG_GETARG_POINTER(0); + + float *size = (float *) PG_GETARG_POINTER(1); + GEOMETRY *a; + float xdim,ydim; + + if (aptr == NULL) + { + *size = 0.0; + //printf(" + aprt==null return in 0.0\n"); + PG_RETURN_VOID(); + } + + a = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + if (a->bvol.URT.x <= a->bvol.LLB.x || + a->bvol.URT.y <= a->bvol.LLB.y) + *size = 0.0; + else + { + xdim = (a->bvol.URT.x - a->bvol.LLB.x); + ydim = (a->bvol.URT.y - a->bvol.LLB.y); + + *size = (float) (xdim * ydim); + } + + //printf(" + about to return (and free) with %e\n",*size); + + /* Avoid leaking memory when handed toasted input. */ + PG_FREE_IF_COPY(a, 0); + + PG_RETURN_VOID(); +} diff --git a/hwgeom/postgis_proj.c b/hwgeom/postgis_proj.c new file mode 100644 index 000000000..9d09440da --- /dev/null +++ b/hwgeom/postgis_proj.c @@ -0,0 +1,646 @@ + +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log$ + * Revision 1.1 2004/09/20 07:50:06 strk + * prepared to contain old internal representation code + * + * Revision 1.14 2004/09/16 15:50:59 mleslie + * Added the distance_sphere function to calculate the distance between two points + * on an earth-sized sphere using an algorithm implemented by Bruno Wolff III. + * Added the postgresql loader function. + * + * Revision 1.13 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.12 2004/02/06 00:42:25 dblasby + * moved forward declarations from postgis.h to postgis_proj.c + * + * Revision 1.11 2004/02/05 20:31:48 dblasby + * Optimized the curvature method (doesnt have to calculate e2) + * + * Revision 1.10 2004/02/05 20:21:14 dblasby + * Added 'curvature method' for cases where the original algorithm breaks down. + * + * Revision 1.9 2004/02/04 02:53:20 dblasby + * applied patricia tozer's patch (distance function was taking acos of something + * just slightly outside [-1,1]). + * + * Revision 1.8 2003/12/04 18:58:35 dblasby + * changed david skae to skea + * + * Revision 1.7 2003/07/01 18:30:55 pramsey + * Added CVS revision headers. + * + * + **********************************************************************/ + +#include "postgres.h" + + +#include +#include +#include +#include +#include + +#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; inpoints;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; inpoints;i++) + { + to = &line->points[i]; + + dist_ellipse = distance_ellipse(frm->y*M_PI/180.0 , frm->x*M_PI/180.0, + to->y*M_PI/180.0 , to->x*M_PI/180.0, + sphere); + + dist += sqrt(dist_ellipse*dist_ellipse + (frm->z*frm->z) ); + + frm = to; + } + return dist; + + +} + + +// length_ellipsoid(GEOMETRY, SPHEROID) +// find the "length of a geometry" +// length2d(point) = 0 +// length2d(line) = length of line +// length2d(polygon) = 0 +// uses ellipsoidal math to find the distance +//// x's are longitude, and y's are latitude - both in decimal degrees + +PG_FUNCTION_INFO_V1(length_ellipsoid); +Datum length_ellipsoid(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + SPHEROID *sphere = (SPHEROID *) PG_GETARG_POINTER(1); + + int32 *offsets1; + char *o1; + int32 type1,j; + LINE3D *line; + double dist = 0.0; + + offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ; + + + //now have to do a scan of each object + + for (j=0; j< geom->nobjs; j++) //for each object in geom1 + { + o1 = (char *) geom +offsets1[j] ; + type1= geom->objType[j]; + if (type1 == LINETYPE) //LINESTRING + { + line = (LINE3D *) o1; + dist += length2d_ellipse_linestring(line,sphere); + } + } + PG_RETURN_FLOAT8(dist); + + + +} + +// length3d_ellipsoid(GEOMETRY, SPHEROID) +// find the "length of a geometry" +// length3d(point) = 0 +// length3d(line) = length of line +// length3d(polygon) = 0 +// uses ellipsoidal math to find the distance on the XY plane, then +// uses simple Pythagoras theorm to find the 3d distance on each segment +// x's are longitude, and y's are latitude - both in decimal degrees + +PG_FUNCTION_INFO_V1(length3d_ellipsoid); +Datum length3d_ellipsoid(PG_FUNCTION_ARGS) +{ + GEOMETRY *geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + SPHEROID *sphere = (SPHEROID *) PG_GETARG_POINTER(1); + + int32 *offsets1; + char *o1; + int32 type1,j; + LINE3D *line; + double dist = 0.0; + + offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ; + + + //now have to do a scan of each object + + for (j=0; j< geom->nobjs; j++) //for each object in geom1 + { + o1 = (char *) geom +offsets1[j] ; + type1= geom->objType[j]; + if (type1 == LINETYPE) //LINESTRING + { + line = (LINE3D *) o1; + dist += length3d_ellipse_linestring(line,sphere); + } + } + PG_RETURN_FLOAT8(dist); +} + +/* + * This algorithm was taken from the geo_distance function of the + * earthdistance package contributed by Bruno Wolff III. + * It was altered to accept GEOMETRY objects and return results in + * meters. + */ +PG_FUNCTION_INFO_V1(distance_sphere); +Datum distance_sphere(PG_FUNCTION_ARGS) +{ + const double EARTH_RADIUS = 6370986.884258304; + const double TWO_PI = 2.0 * M_PI; + GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GEOMETRY *geom2 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + POINT3D *pt1, *pt2; + int32 *offsets1; + int32 *offsets2; + char *o; + + double long1, lat1, long2, lat2; + double longdiff; + double sino; + + if (geom1->SRID != geom2->SRID) + { + elog(ERROR, "optimistic_overlap:Operation on two GEOMETRIES with differenc SRIDs\n"); + PG_RETURN_NULL(); + } + + if (geom1->type != POINTTYPE) + { + elog(ERROR, "optimistic_overlap: first arg isnt a point\n"); + PG_RETURN_NULL(); + } + + if (geom2->type != POINTTYPE) + { + elog(ERROR, "optimistic_overlap: second arg isnt a point\n"); + PG_RETURN_NULL(); + } + + offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ); + offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs ); + o = (char *) geom1 + offsets1[0]; + pt1 = (POINT3D *) o; + + o = (char *) geom2 + offsets2[0]; + pt2 = (POINT3D *) o; + + /* + * Start geo_distance code. Longitude is degrees west of + * Greenwich, and thus is negative from what normal things + * will supply the function. + */ + long1 = -1 * (pt1->x / 360.0) * TWO_PI; + lat1 = (pt1->y / 360.0) * TWO_PI; + + long2 = -1 * (pt2->x / 360.0) * TWO_PI; + lat2 = (pt2->y / 360.0) * TWO_PI; + + /* compute difference in longitudes - want < 180 degrees */ + longdiff = fabs(long1 - long2); + if (longdiff > M_PI) + longdiff = TWO_PI - longdiff; + + sino = sqrt(sin(fabs(lat1 - lat2) / 2.) * sin(fabs(lat1 - lat2) / 2.) + + cos(lat1) * cos(lat2) * sin(longdiff / 2.) * sin(longdiff / 2.)); + if (sino > 1.) + sino = 1.; + PG_RETURN_FLOAT8(2. * EARTH_RADIUS * asin(sino)); + +/* PG_RETURN_FLOAT8(distance_sphere_method(pt1->y*M_PI/180.0 , + pt1->x*M_PI/180.0 , + pt2->y*M_PI/180.0 , + pt2->x*M_PI/180.0 , + sphere)); +*/ + +} + + +//distance (geometry,geometry, sphere) +// -geometrys MUST be points +PG_FUNCTION_INFO_V1(distance_ellipsoid); +Datum distance_ellipsoid(PG_FUNCTION_ARGS) +{ + SPHEROID *sphere = (SPHEROID *) PG_GETARG_POINTER(2); + GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GEOMETRY *geom2 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + + POINT3D *pt1,*pt2; + int32 *offsets1; + int32 *offsets2; + char *o; + + + if (geom1->SRID != geom2->SRID) + { + elog(ERROR,"optimistic_overlap:Operation on two GEOMETRIES with different SRIDs\n"); + PG_RETURN_NULL(); + } + + if (geom1->type != POINTTYPE) + { + elog(ERROR,"optimistic_overlap: first arg isnt a point\n"); + PG_RETURN_NULL(); + } + if (geom2->type != POINTTYPE) + { + elog(ERROR,"optimistic_overlap: second arg isnt a point\n"); + PG_RETURN_NULL(); + } + + offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ; + offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs ) ; + o = (char *) geom1 +offsets1[0] ; + pt1 = (POINT3D *) o; + + o = (char *) geom2 +offsets2[0] ; + pt2 = (POINT3D *) o; + + PG_RETURN_FLOAT8(distance_ellipse(pt1->y*M_PI/180.0 ,pt1->x*M_PI/180.0 , + pt2->y*M_PI/180.0 ,pt2->x*M_PI/180.0 , sphere) ); + +//double distance_ellipse(double lat1, double long1, +// double lat2, double long2, +// SPHEROID *sphere) + + +} + + +/* + * For some lat/long points, the above method doesnt calculate the distance very well. + * Typically this is for two lat/long points that are very very close together (<10cm). + * This gets worse closer to the equator. + * + * This method works very well for very close together points, not so well if they're + * far away (>1km). + * + * METHOD: + * We create two circles (with Radius R and Radius S) and use these to calculate the distance. + * + * The first (R) is basically a (north-south) line of longitude. + * Its radius is approximated by looking at the ellipse. Near the equator R = 'a' (earth's major axis) + * near the pole R = 'b' (earth's minor axis). + * + * The second (S) is basically a (east-west) line of lattitude. + * Its radius runs from 'a' (major axis) at the equator, and near 0 at the poles. + * + * + * North pole + * * + * * + * *\--S-- + * * R + + * * \ + + * * A\ + + * * ------ \ Equator/centre of earth + * * + * * + * * + * * + * * + * * + * South pole + * (side view of earth) + * + * Angle A is lat1 + * R is the distance from the centre of the earth to the lat1/long1 point on the surface + * of the Earth. + * S is the circle-of-lattitude. Its calculated from the right triangle defined by + * the angle (90-A), and the hypothenus R. + * + * + * + * Once R and S have been calculated, the actual distance between the two points can be + * calculated. + * + * We dissolve the vector from lat1,long1 to lat2,long2 into its X and Y components (called DeltaX,DeltaY). + * The actual distance that these angle-based measurements represent is taken from the two + * circles we just calculated; R (for deltaY) and S (for deltaX). + * + * (if deltaX is 1 degrees, then that distance represents 1/360 of a circle of radius S.) + * + * + * Parts taken from PROJ4 - geodetic_to_geocentric() (for calculating Rn) + * + * remember that lat1/long1/lat2/long2 are comming in a *RADIANS* not degrees. + * + * By Patricia Tozer and Dave Blasby + * + * This is also called the "curvature method". + */ + +double distance_sphere_method(double lat1, double long1,double lat2,double long2, SPHEROID *sphere) +{ + double R,S,X,Y,deltaX,deltaY; + + double distance = 0.0; + double sin_lat = sin(lat1); + double sin2_lat = sin_lat * sin_lat; + + double Geocent_a = sphere->a; + double Geocent_e2 = sphere->e_sq; + + R = Geocent_a / (sqrt(1.0e0 - Geocent_e2 * sin2_lat)); + S = R * sin(M_PI/2.0-lat1) ; // 90 - lat1, but in radians + + deltaX = long2 - long1; //in rads + deltaY = lat2 - lat1; // in rads + + X = deltaX/(2.0*M_PI) * 2 * M_PI * S; // think: a % of 2*pi*S + Y = deltaY /(2.0*M_PI) * 2 * M_PI * R; + + distance = sqrt((X * X + Y * Y)); + + return distance; +} diff --git a/hwgeom/postgis_svg.c b/hwgeom/postgis_svg.c new file mode 100644 index 000000000..cb14bfcb5 --- /dev/null +++ b/hwgeom/postgis_svg.c @@ -0,0 +1,352 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of hte GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * + * SVG output routines. + * Originally written by: Klaus Förster + * Patches from: Olivier Courtin + * + ********************************************************************** + * $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;tnobjs; 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 +#include +#include +#include +#include + +#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;tvl_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; inrings;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 -- 2.40.0