]> granicus.if.org Git - postgis/commitdiff
Added support for histogram2d and better statistical analysis.
authorDavid Blasby <dblasby@gmail.com>
Fri, 11 Oct 2002 22:52:06 +0000 (22:52 +0000)
committerDavid Blasby <dblasby@gmail.com>
Fri, 11 Oct 2002 22:52:06 +0000 (22:52 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@196 b70326c6-7e19-0410-871a-916f4a2858ee

Makefile
postgis.h
postgis.sql.in
postgis_gist_72.c
postgis_gist_72.sql.in
postgis_inout.c

index b59de603b031ba991ae7fa0cf529e4499854a3d0..5e4a717a87b8d5268b856e3bb15016205c8de21a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,10 @@
 # Configuration Directives
-
+#---------------------------------------------------------------
+# Set USE_PG72 to 1 for PostgreSQL version >= 7.2
+USE_PG72=0
 #---------------------------------------------------------------
 # Set USE_PROJ to 1 for Proj4 reprojection support
-USE_PROJ=0
+USE_PROJ=1
 
 #---------------------------------------------------------------
 subdir=contrib/postgis
@@ -21,17 +23,6 @@ else
        libdir := ${PWD}
 endif
 
-#---------------------------------------------------------------
-# Test the version string and select the correct GiST index
-# bindings.
-ifneq ($(findstring 7.1,$(VERSION)),)
-       USE_PG72=0
-else
-       USE_PG72=1
-endif
-
-#---------------------------------------------------------------
-# Regression test temporary database.
 TEST_DB=geom_regress
 
 #---------------------------------------------------------------
@@ -50,9 +41,9 @@ endif
 override DLLLIBS := $(BE_DLLLIBS) $(DLLLIBS)
 
 ifeq ($(USE_PG72),1)
-       OBJS=postgis_debug.o postgis_ops.o postgis_fn.o postgis_inout.o postgis_proj.o postgis_chip.o postgis_transform.o postgis_gist_72.o
+       OBJS=postgis_debug.o postgis_ops.o postgis_fn.o postgis_inout.o postgis_proj.o postgis_chip.o postgis_transform.o postgis_gist_72.o postgis_estimate.o
 else
-       OBJS=postgis_debug.o postgis_ops.o postgis_fn.o postgis_inout.o postgis_proj.o postgis_chip.o postgis_transform.o postgis_gist.o
+       OBJS=postgis_debug.o postgis_ops.o postgis_fn.o postgis_inout.o postgis_proj.o postgis_chip.o postgis_transform.o postgis_gist.o 
 endif
 
 # Add libraries that libpq depends (or might depend) on into the
index a6a4ade1d89ea84eeaf2cbba74a92496362db1c4..07e77a795935f5369056fee95973ab41b361e920 100644 (file)
--- a/postgis.h
+++ b/postgis.h
@@ -242,6 +242,29 @@ typedef struct Well_known_bin {
        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
 
@@ -498,6 +521,18 @@ 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);
+
+Datum postgisgistcostestimate(PG_FUNCTION_ARGS);
 
 /*--------------------------------------------------------------------
  * Useful floating point utilities and constants.
index a8795abab498fece28ad58e94fdd74a73abead28..449a32b2a54e48691b1b6dcecd7f73b9f545e8ed 100644 (file)
@@ -18,6 +18,40 @@ as 'select \'@POSTGIS_VERSION@\'::text as version'
 LANGUAGE 'sql';
 
 
+-- need this to define geometry_columns table
+create function histogram2d_in(opaque)
+       RETURNS HISTOGRAM2D
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'c' with (isstrict);
+
+create function histogram2d_out(opaque)
+       RETURNS opaque
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'c' with (isstrict);
+
+CREATE TYPE HISTOGRAM2D (
+       alignment = double,
+       internallength = VARIABLE,
+       input =  histogram2d_in,
+       output =  histogram2d_out,
+       storage = main
+);
+
+
+
+
+--drop function FIX_GEOMETRY_COLUMNS() ;
+--utility function to fixup the geometry_columns table with system table information
+CREATE FUNCTION FIX_GEOMETRY_COLUMNS() returns text
+as 
+'
+BEGIN
+       EXECUTE ''update geometry_columns set attrelid = (select pg_class.oid as attrelid from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name  and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name),  varattnum = (select pg_attribute.attnum from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name)'';
+       RETURN ''GEOMETRY_COLUMNS table is now linked to the system tables'';
+END;
+'
+       LANGUAGE 'plpgsql' ;
+
 -- create the table with spatial referencing information in it. spec, section 3.2.1.2
 
 create table spatial_ref_sys (
@@ -38,6 +72,9 @@ f_geometry_column varchar(256) not null,
 coord_dimension  integer NOT NULL,
 srid           integer NOT NULL,
 type           varchar(30) NOT NULL,
+attrelid oid,
+varattnum int,
+stats HISTOGRAM2D,
 CONSTRAINT GC_PK primary key ( f_table_catalog,f_table_schema, f_table_name,f_geometry_column)
 ) ;
 
@@ -118,11 +155,11 @@ BEGIN
 
        -- update the given table/column so that it it all NULLS
 
-       EXECUTE ''update ''||table_name||'' set ''||column_name||''= NULL'';
+       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)'';
+       EXECUTE ''ALTER TABLE "''||table_name||''" ADD CHECK ("''||column_name||''" IS NULL)'';
 
        RETURN table_name || ''.'' || column_name ||'' effectively removed.'';
        
@@ -187,19 +224,20 @@ BEGIN
                return ''fail'';
        end if;
 
-       EXECUTE ''ALTER TABLE '' || table_name || '' ADD COLUMN '' || column_name || '' GEOMETRY '';
+       EXECUTE ''ALTER TABLE "'' || table_name || ''" ADD COLUMN "'' || column_name || ''" GEOMETRY '';
        EXECUTE ''INSERT INTO   geometry_columns VALUES ('' || quote_literal('''') || '','' ||
                         quote_literal(database_name) || '','' || quote_literal(table_name) || '','' ||
                         quote_literal(column_name) || '','' ||
                         new_dim ||'',''||new_srid||'',''||quote_literal(new_type)||'')'';
 
+       EXECUTE ''update geometry_columns set attrelid = (select pg_class.oid as attrelid from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name  and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name),  varattnum = (select pg_attribute.attnum from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name)'';
 
  
-       EXECUTE ''ALTER TABLE '' ||table_name||'' ADD CHECK (SRID('' || column_name ||
-               '') = '' || new_srid || '')'' ;
+       EXECUTE ''ALTER TABLE "'' ||table_name||''" ADD CHECK (SRID("'' || column_name ||
+               ''") = '' || new_srid || '')'' ;
 
        IF (not(new_type = ''GEOMETRY'')) THEN
-               EXECUTE ''ALTER TABLE '' ||table_name||'' ADD CHECK ( geometrytype(''||column_name||'')=''|| quote_literal(new_type)||'' OR ('' ||column_name ||'') is null)'';
+               EXECUTE ''ALTER TABLE "'' ||table_name||''" ADD CHECK ( geometrytype("''||column_name||''")=''|| quote_literal(new_type)||'' OR ('' ||column_name ||'') is null)'';
        END IF;
 
        return ''Geometry column '' || column_name || '' added to table ''
@@ -286,8 +324,30 @@ CREATE TYPE CHIP (
        storage= extended
 );
 
+--drop function find_extent(text,text);
+-- FIND_EXTENT(table name,column name)
+CREATE FUNCTION 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);
+--select find_extent('test_data','the_geom');
 
 
+--CREATE FUNCTION get_proj4_from_srid(integer) returns text as
+--'SELECT proj4text::text FROM spatial_ref_sys WHERE srid= $1' 
+--LANGUAGE 'sql' with (iscachable,isstrict);
+
 
 
 
@@ -380,13 +440,17 @@ create function geometry(CHIP)
    AS '@MODULE_FILENAME@','CHIP_to_geom'
    LANGUAGE 'c' with (isstrict,iscachable);
 
-
-
 CREATE FUNCTION box3d(GEOMETRY)
    RETURNS BOX3D
    AS '@MODULE_FILENAME@','get_bbox_of_geometry'
             LANGUAGE 'c' WITH (iscachable,isstrict);
 
+
+CREATE FUNCTION box(GEOMETRY)
+   RETURNS BOX
+   AS '@MODULE_FILENAME@','geometry2box'
+            LANGUAGE 'c' WITH (iscachable,isstrict);
+
 CREATE FUNCTION geometry(BOX3D)
    RETURNS GEOMETRY
    AS '@MODULE_FILENAME@','get_geometry_of_bbox'
@@ -762,6 +826,6 @@ CREATE FUNCTION force_collection(GEOMETRY) RETURNS GEOMETRY
 --- workaround for user defined VARIABLE length datatype default value bug
 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';
 
 end TRANSACTION;
index 6c31b829ac8650721330e88303d9ed48c731b304..33595b4eb30ecb26cc945fdeecd4200bf5e1b8ca 100644 (file)
@@ -2,11 +2,11 @@
  postGIS - geometric types for postgres
 
  This software is copyrighted (2001).
+
  This is free software; you can redistribute it and/or modify
  it under the GNU General Public Licence.  See the file "COPYING".
 
- More Info?  See the documentation, join the mailing list 
+ More Info?  See the documentation, join the mailing list
  (postgis@yahoogroups.com), or see the web page
  (http://postgis.refractions.net).
 
@@ -31,7 +31,7 @@
 #include "utils/elog.h"
 
 //Norman Vine found this problem for compiling under cygwin
-//  it defines BYTE_ORDER and LITTLE_ENDIAN 
+//  it defines BYTE_ORDER and LITTLE_ENDIAN
 
 #ifdef __CYGWIN__
 #include <sys/param.h>       // FOR ENDIAN DEFINES
@@ -61,12 +61,7 @@ static float size_box(Datum box);
 
 int debug = 0;
 
-//restriction in the GiST && operator
-PG_FUNCTION_INFO_V1(postgis_gist_sel);
-Datum postgis_gist_sel(PG_FUNCTION_ARGS)
-{
-        PG_RETURN_FLOAT8(0.000005);
-}
+
 
 BOX    *convert_box3d_to_box(BOX3D *in)
 {
@@ -87,7 +82,7 @@ Datum ggeometry_compress(PG_FUNCTION_ARGS)
        GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0);
        GISTENTRY *retval;
 
-       if ( entry->leafkey) 
+       if ( entry->leafkey)
        {
                retval = palloc(sizeof(GISTENTRY));
                if ( DatumGetPointer(entry->key) != NULL ) {
@@ -102,7 +97,7 @@ Datum ggeometry_compress(PG_FUNCTION_ARGS)
 
                        in = (GEOMETRY*)PG_DETOAST_DATUM( entry->key );
                        r = convert_box3d_to_box(&in->bvol);
-                       if ( in != (GEOMETRY*)DatumGetPointer(entry->key) ) 
+                       if ( in != (GEOMETRY*)DatumGetPointer(entry->key) )
                        {
                                pfree( in );
                        }
@@ -113,7 +108,7 @@ Datum ggeometry_compress(PG_FUNCTION_ARGS)
                } else {
                        gistentryinit(*retval, (Datum) 0, entry->rel, entry->page,
                                entry->offset, 0,FALSE);
-               } 
+               }
        } else {
                retval = entry;
        }
@@ -266,7 +261,7 @@ typedef struct {
 
 static int
 compare_KB(const void* a, const void* b) {
-       BOX *abox = ((KBsort*)a)->key; 
+       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);
@@ -322,13 +317,13 @@ gbox_picksplit(PG_FUNCTION_ARGS)
        {
                cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
                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 
-                       ) ) 
+                               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)
@@ -540,3 +535,4 @@ size_box(Datum box)
                return 0.0;
 }
 
+
index 5ce89617d58c36817848c5c63b99a87f75d66109..4aec32cdb8e9ed57e65022011713491c9a34caaa 100644 (file)
@@ -1,5 +1,74 @@
 BEGIN TRANSACTION;
 
+--drop function UPDATE_GEOMETRY_STATS();
+-- UPDATE_GEOMETRY_STATS()  -- all tables
+CREATE FUNCTION UPDATE_GEOMETRY_STATS() returns text
+AS
+'
+BEGIN
+       EXECUTE ''update geometry_columns set attrelid = (select pg_class.oid as attrelid from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name  and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name),  varattnum = (select pg_attribute.attnum from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name)'';
+       execute ''update geometry_columns set stats = (build_histogram2d( create_histogram2d(find_extent(f_table_name,f_geometry_column),40 ),f_table_name::text, f_geometry_column::text))  '';        
+       return ''done'';
+END;
+'
+LANGUAGE 'plpgsql' ;
+--select UPDATE_GEOMETRY_STATS();
+
+
+--drop function UPDATE_GEOMETRY_STATS(varchar,varchar);
+-- UPDATE_GEOMETRY_STATS(table name,column name)
+CREATE FUNCTION UPDATE_GEOMETRY_STATS(varchar,varchar) returns text
+AS
+'
+DECLARE
+       tablename alias for $1;
+       columnname alias for $2;
+
+BEGIN
+       EXECUTE ''update geometry_columns set attrelid = (select pg_class.oid as attrelid from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name  and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name),  varattnum = (select pg_attribute.attnum from pg_class,pg_attribute where relname =geometry_columns.f_table_name::name and pg_attribute.attrelid = pg_class.oid and pg_attribute.attname = geometry_columns.f_geometry_column::name)'';
+       execute ''update geometry_columns set stats = (build_histogram2d( create_histogram2d(find_extent(''|| quote_literal(tablename) || '',''||quote_literal(columnname) ||''),40 ),''|| quote_literal(tablename) || ''::text,''||quote_literal(columnname) ||''::text )) WHERE f_table_name=''|| quote_literal(tablename) || ''and f_geometry_column=''||quote_literal(columnname) ; 
+       return ''done'';
+END;
+'
+LANGUAGE 'plpgsql' ;
+--select UPDATE_GEOMETRY_STATS('test_data','the_geom');
+
+
+
+
+--- create_histogram2d(BOX3D, boxesPerSide)
+--- returns a histgram with 0s in all the boxes.
+CREATE FUNCTION create_histogram2d(box3d,int)
+   RETURNS HISTOGRAM2D
+   AS '@MODULE_FILENAME@','create_histogram2d'
+            LANGUAGE 'c'  with (isstrict);
+
+
+---- build_histogram2d (HISTOGRAM2D, tablename, columnname)
+CREATE FUNCTION build_histogram2d (HISTOGRAM2D,text,text)
+   RETURNS HISTOGRAM2D
+   AS '@MODULE_FILENAME@','build_histogram2d'
+            LANGUAGE 'c'  with (isstrict);
+
+---- explode_histogram2d(HISTOGRAM2D, tablename)
+CREATE FUNCTION explode_histogram2d (HISTOGRAM2D,text)
+   RETURNS HISTOGRAM2D
+   AS '@MODULE_FILENAME@','explode_histogram2d'
+            LANGUAGE 'c'  with (isstrict);
+
+---- estimate_histogram2d(HISTOGRAM2D, box)
+CREATE FUNCTION estimate_histogram2d(HISTOGRAM2D,box)
+   RETURNS FLOAT8
+   AS '@MODULE_FILENAME@','estimate_histogram2d'
+            LANGUAGE 'c'  with (isstrict);
+
+
+
+create function postgisgistcostestimate(opaque,opaque,opaque,opaque,opaque,opaque,opaque,opaque)
+   RETURNS opaque
+   AS '@MODULE_FILENAME@','postgisgistcostestimate'
+            LANGUAGE 'c'  with (isstrict);
+
 -------- 7.2 GiST support functions
 create function ggeometry_consistent(opaque,GEOMETRY,int4) returns bool 
 as '@MODULE_FILENAME@'   language 'C';
index b22f03b5760119469d82c4ada1b2ca145fe49485..2c53f5617625bacfd6b02535aacaab48c9d9888c 100644 (file)
@@ -3413,3 +3413,183 @@ Datum WKBtoBYTEA(PG_FUNCTION_ARGS)
 
                PG_RETURN_POINTER(result);
 }
+
+PG_FUNCTION_INFO_V1(geometry2box);
+Datum geometry2box(PG_FUNCTION_ARGS)
+{
+        GEOMETRY *g = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+//elog(NOTICE,"geometry2box - ymax is %.15g",g->bvol.URT.y);
+
+       PG_RETURN_POINTER(convert_box3d_to_box(&g->bvol) );
+
+}
+
+//create_histogram2d(BOX3D, boxesPerSide)
+// returns a histgram with 0s in all the boxes.
+PG_FUNCTION_INFO_V1(create_histogram2d);
+Datum create_histogram2d(PG_FUNCTION_ARGS)
+{
+       BOX3D  *bbox = (BOX3D *) PG_GETARG_POINTER(0);
+       int32   boxesPerSide=  PG_GETARG_INT32(1);
+       HISTOGRAM2D             *histo;
+       int size,t;
+
+
+       if ( (boxesPerSide <1) || (boxesPerSide >50) )
+       {
+                       elog(ERROR,"create_histogram2d - boxesPerSide is too small or big.\n");
+                       PG_RETURN_NULL() ;
+       }
+
+
+       size =  sizeof(HISTOGRAM2D) +    (boxesPerSide*boxesPerSide-1)*4  ;
+
+       histo = (HISTOGRAM2D *) palloc (size);
+       histo->size = size;
+
+       histo->xmin = bbox->LLB.x;
+       histo->ymin = bbox->LLB.y;
+
+
+       histo->xmax = bbox->URT.x;
+       histo->ymax = bbox->URT.y;
+
+       histo->avgFeatureArea = 0;
+
+       histo->boxesPerSide = boxesPerSide;
+
+       for (t=0;t<boxesPerSide*boxesPerSide; t++)
+       {
+               histo->value[t] =0;
+       }
+
+       //elog(NOTICE,"create_histogram2d returning");
+
+       PG_RETURN_POINTER(histo);
+
+}
+
+
+//text form of HISTOGRAM2D is:
+// 'HISTOGRAM2D(xmin,ymin,xmax,ymax,boxesPerSide;value[0],value[1],...')
+//    note the ";" in the middle (for easy parsing)
+//  I dont expect anyone to actually create one by hand
+PG_FUNCTION_INFO_V1(histogram2d_in);
+Datum histogram2d_in(PG_FUNCTION_ARGS)
+{
+       char                    *str = PG_GETARG_CSTRING(0);
+       HISTOGRAM2D         *histo ;
+       int nitems;
+       double xmin,ymin,xmax,ymax;
+       int boxesPerSide;
+       double avgFeatureArea;
+       char *str2,*str3;
+       long datum;
+
+       int t;
+
+       while (isspace((unsigned char) *str))
+               str++;
+
+       if (strstr(str,"HISTOGRAM2D(") != str)
+       {
+                       elog(ERROR,"histogram2d parser - doesnt start with 'HISTOGRAM2D(\n");
+                       PG_RETURN_NULL() ;
+       }
+       if (strstr(str,";") == NULL)
+       {
+                       elog(ERROR,"histogram2d parser - doesnt have a ; in sring!\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       nitems = sscanf(str,"HISTOGRAM2D(%lf,%lf,%lf,%lf,%i,%lf;",&xmin,&ymin,&xmax,&ymax,&boxesPerSide,&avgFeatureArea);
+
+       if (nitems != 6)
+       {
+                       elog(ERROR,"histogram2d parser - couldnt parse initial portion of histogram!\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       if  ( (boxesPerSide > 50) || (boxesPerSide <1) )
+       {
+                       elog(ERROR,"histogram2d parser - boxesPerSide is too big or too small\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       str2 = strstr(str,";");
+       str2++;
+
+       if (str2[0] ==0)
+       {
+                       elog(ERROR,"histogram2d parser - no histogram values\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       histo = (HISTOGRAM2D *) palloc (sizeof(HISTOGRAM2D) +    (boxesPerSide*boxesPerSide-1)*4 );
+       histo->size = sizeof(HISTOGRAM2D) +    (boxesPerSide*boxesPerSide-1)*4  ;
+
+       for (t=0;t<boxesPerSide*boxesPerSide;t++)
+       {
+               datum = strtol(str2,&str3,10); // str2=start of int, str3=end of int, base 10
+               // str3 points to "," or ")"
+               if (str3[0] ==0)
+               {
+                       elog(ERROR,"histogram2d parser - histogram values prematurely ended!\n");
+                       PG_RETURN_NULL() ;
+               }
+               histo->value[t] = (unsigned int) datum;
+               str2= str3+1; //move past the "," or ")"
+       }
+       histo->xmin = xmin;
+       histo->xmax = xmax;
+       histo->ymin = ymin;
+       histo->ymax = ymax;
+       histo->avgFeatureArea = avgFeatureArea;
+       histo->boxesPerSide = boxesPerSide;
+
+       PG_RETURN_POINTER(histo);
+}
+
+
+
+//text version
+PG_FUNCTION_INFO_V1(histogram2d_out);
+Datum histogram2d_out(PG_FUNCTION_ARGS)
+{
+       //char *result;
+       //result = palloc(200);
+       //sprintf(result,"HISTOGRAM2D(0,0,100,100,2;11,22,33,44)");
+       //PG_RETURN_CSTRING(result);
+
+               HISTOGRAM2D   *histo = (HISTOGRAM2D *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               char    *result;
+               int t;
+               char    temp[100];
+               int size;
+
+               size = 26+6*MAX_DIGS_DOUBLE + histo->boxesPerSide*histo->boxesPerSide* (MAX_DIGS_DOUBLE+1);
+               result = palloc(size);
+
+               sprintf(result,"HISTOGRAM2D(%.15g,%.15g,%.15g,%.15g,%i,%.15g;",
+                                       histo->xmin,histo->ymin,histo->xmax,histo->ymax,histo->boxesPerSide,histo->avgFeatureArea );
+
+               //elog(NOTICE,"so far: %s",result);
+               //elog(NOTICE,"buffsize=%i, size=%i",size,histo->size);
+
+               for (t=0;t<histo->boxesPerSide*histo->boxesPerSide;t++)
+               {
+                       if (t!=((histo->boxesPerSide*histo->boxesPerSide)-1))
+                               sprintf(temp,"%u,", histo->value[t]);
+                       else
+                               sprintf(temp,"%u", histo->value[t]);
+                       strcat(result,temp);
+               }
+
+               strcat(result,")");
+               //elog(NOTICE,"about to return string (len=%i): -%s-",strlen(result),result);
+
+               PG_RETURN_CSTRING(result);
+
+}
+