[geos-devel] patch : TIN & POLYHEDRALSURFACE

Mathieu Le Serre mathieu.leserre at camptocamp.com
Mon Sep 24 13:13:59 EDT 2007


Hi,

A few month ago, we already discussed here about 3D storage and PostGIS,
(cf
<http://postgis.refractions.net/pipermail/postgis-users/2007-April/015166.html>)
 
We made a patch to add such new geometries to PostGIS.

It implements TIN and POLYHEDRALSURFACE as described in
OGC SFS1.2 standard.
These geometries are specific MULTIPOLYGON that
describe 3D surfaces and 3D volumes.

Currently, we got the following functions to work for 3D geometries:
- asEwkb
- asEwkt
- asGml
- asSvg (2D output only)
- asX3d.

This last function enables exporting 3D geometries to the X3D format.

I also provide a patch for GEOS to implement the isValid function
for TIN and POLYHEDRALSURFACE. It checks if feature faces
are well connected and well oriented.

A presentation around this work will be done in FOSS4G
<http://www.foss4g2007.org/presentations/view.php?abstract_id=224>

PostGIS implementation consideration:
There were already 15 geometries types and I needed to add two. But geometry
types were coded on 4 bits only...
In order to implement these geometries,  I decided to drop three unused
geometry types (POINTTYPEI, LINETYPEI, and POLYGONTYPEI).
They were not present in the postgis parser, and were described as
"Invalid types" in the file liblwgeom.c.


Any comments and/or reactions would be greatly appreciated.
  

Best regards,

 
-------------- next part --------------
--- postgis-svn/lwgeom/liblwgeom.h	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/liblwgeom.h	2007-09-18 15:50:59.000000000 +0200
@@ -293,6 +293,26 @@
 	LWPOLY **geoms;
 } LWMPOLY; 
 
+/* TINTYPE */
+typedef struct
+{
+        uchar type;
+        BOX2DFLOAT4 *bbox;
+        uint32 SRID;
+        int  ngeoms;
+        LWPOLY **geoms;
+} LWTIN;
+
+/* POLYHEDRALSURFACETYPE */
+typedef struct
+{
+        uchar type;
+        BOX2DFLOAT4 *bbox;
+        uint32 SRID;
+        int  ngeoms;
+        LWPOLY **geoms;
+} LWPOLYHEDRALSURFACE;
+
 /* COLLECTIONTYPE */
 typedef struct
 {   
@@ -305,6 +325,8 @@
 
 /* Casts LWGEOM->LW* (return NULL if cast is illegal) */
 extern LWMPOLY *lwgeom_as_lwmpoly(LWGEOM *lwgeom);
+extern LWTIN *lwgeom_as_lwtin(LWGEOM *lwgeom);
+extern LWPOLYHEDRALSURFACE *lwgeom_as_lwpolyhedralsurface(LWGEOM *lwgeom);
 extern LWMLINE *lwgeom_as_lwmline(LWGEOM *lwgeom);
 extern LWMPOINT *lwgeom_as_lwmpoint(LWGEOM *lwgeom);
 extern LWCOLLECTION *lwgeom_as_lwcollection(LWGEOM *lwgeom);
@@ -314,6 +336,8 @@
 
 /* Casts LW*->LWGEOM (always cast) */
 extern LWGEOM *lwmpoly_as_lwgeom(LWMPOLY *obj);
+extern LWGEOM *lwtin_as_lwgeom(LWTIN *obj);
+extern LWGEOM *lwpolyhedralsurface_as_lwgeom(LWPOLYHEDRALSURFACE *obj);
 extern LWGEOM *lwmline_as_lwgeom(LWMLINE *obj);
 extern LWGEOM *lwmpoint_as_lwgeom(LWMPOINT *obj);
 extern LWGEOM *lwcollection_as_lwgeom(LWCOLLECTION *obj);
@@ -446,6 +470,8 @@
 #define	MULTILINETYPE	5
 #define	MULTIPOLYGONTYPE	6
 #define	COLLECTIONTYPE	7
+#define TINTYPE		10
+#define POLYHEDRALSURFACETYPE	11
 
 #define WKBZOFFSET 0x80000000
 #define WKBMOFFSET 0x40000000
@@ -688,6 +714,8 @@
 LWMPOINT *lwmpoint_deserialize(uchar *serializedform);
 LWMLINE *lwmline_deserialize(uchar *serializedform);
 LWMPOLY *lwmpoly_deserialize(uchar *serializedform);
+LWTIN *lwtin_deserialize(uchar *serializedform);
+LWPOLYHEDRALSURFACE *lwpolyhedralsurface_deserialize(uchar *serializedform);
 LWCOLLECTION *lwcollection_deserialize(uchar *serializedform);
 LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *, int);
 
@@ -992,6 +1020,8 @@
 LWGEOM *lwline_add(const LWLINE *to, uint32 where, const LWGEOM *what);
 LWGEOM *lwpoly_add(const LWPOLY *to, uint32 where, const LWGEOM *what);
 LWGEOM *lwmpoly_add(const LWMPOLY *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwtin_add(const LWTIN *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwpolyhedralsurface_add(const LWPOLYHEDRALSURFACE *to, uint32 where, const LWGEOM *what);
 LWGEOM *lwmline_add(const LWMLINE *to, uint32 where, const LWGEOM *what);
 LWGEOM *lwmpoint_add(const LWMPOINT *to, uint32 where, const LWGEOM *what);
 LWGEOM *lwcollection_add(const LWCOLLECTION *to, uint32 where, const LWGEOM *what);
--- postgis-svn/lwgeom/lwgeom.c	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/lwgeom.c	2007-09-18 15:13:41.000000000 +0200
@@ -46,6 +46,10 @@
 			return (LWGEOM *)lwmline_deserialize(srl);
 		case MULTIPOLYGONTYPE:
 			return (LWGEOM *)lwmpoly_deserialize(srl);
+		case TINTYPE:
+			return (LWGEOM *)lwtin_deserialize(srl);
+		case POLYHEDRALSURFACETYPE:
+			return (LWGEOM *)lwpolyhedralsurface_deserialize(srl);
 		case COLLECTIONTYPE:
 			return (LWGEOM *)lwcollection_deserialize(srl);
                 case COMPOUNDTYPE:
@@ -92,6 +96,8 @@
 		case MULTILINETYPE:
                 case MULTICURVETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
                 case MULTISURFACETYPE:
 		case COLLECTIONTYPE:
 			return lwcollection_serialize_size((LWCOLLECTION *)lwgeom);
@@ -134,6 +140,8 @@
 		case MULTILINETYPE:
                 case MULTICURVETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
                 case MULTISURFACETYPE:
 		case COLLECTIONTYPE:
 			lwcollection_serialize_buf((LWCOLLECTION *)lwgeom, buf,
@@ -184,6 +192,8 @@
 			return;
 
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			coll = (LWCOLLECTION *)lwgeom;
 			for (i=0; i<coll->ngeoms; i++)
@@ -209,6 +219,8 @@
 			return;
 		case MULTILINETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			col = (LWCOLLECTION *)lwgeom;
 			for (i=0; i<col->ngeoms; i++)
@@ -239,6 +251,8 @@
 		case MULTILINETYPE:
                 case MULTICURVETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
                 case MULTISURFACETYPE:
 		case COLLECTIONTYPE:
 			return lwcollection_compute_box2d_p((LWCOLLECTION *)lwgeom, buf);
@@ -325,7 +339,26 @@
 	else return NULL;
 }
 
+LWTIN *
+lwgeom_as_lwtin(LWGEOM *lwgeom)
+{
+        if ( TYPE_GETTYPE(lwgeom->type) == TINTYPE )
+                return (LWTIN *)lwgeom;
+        else return NULL;
+}
+
+LWPOLYHEDRALSURFACE *
+lwgeom_as_lwpolyhedralsurface(LWGEOM *lwgeom)
+{
+        if ( TYPE_GETTYPE(lwgeom->type) == POLYHEDRALSURFACETYPE )
+                return (LWPOLYHEDRALSURFACE *)lwgeom;
+        else return NULL;
+}
+
+
 LWGEOM *lwmpoly_as_lwgeom(LWMPOLY *obj) { return (LWGEOM *)obj; }
+LWGEOM *lwtin_as_lwgeom(LWTIN *obj) { return (LWGEOM *)obj; }
+LWGEOM *lwpolyhedralsurface_as_lwgeom(LWPOLYHEDRALSURFACE *obj) { return (LWGEOM *)obj; }
 LWGEOM *lwmline_as_lwgeom(LWMLINE *obj) { return (LWGEOM *)obj; }
 LWGEOM *lwmpoint_as_lwgeom(LWMPOINT *obj) { return (LWGEOM *)obj; }
 LWGEOM *lwcollection_as_lwgeom(LWCOLLECTION *obj) { return (LWGEOM *)obj; }
@@ -395,6 +428,8 @@
 		case MULTILINETYPE:
                 case MULTICURVETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
                 case MULTISURFACETYPE:
 		case COLLECTIONTYPE:
 			return (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom);
@@ -462,6 +497,14 @@
 			return (LWGEOM *)lwmpoly_add((const LWMPOLY *)to,
 				where, what);
 
+		 case TINTYPE:
+                        return (LWGEOM *)lwtin_add((const LWTIN *)to,
+                                where, what);
+
+		 case POLYHEDRALSURFACETYPE:
+                        return (LWGEOM *)lwpolyhedralsurface_add((const LWPOLYHEDRALSURFACE *)to,
+                                where, what);
+
                 case MULTISURFACETYPE:
                         return (LWGEOM *)lwmsurface_add((const LWMSURFACE *)to,
                                 where, what);
@@ -618,6 +661,8 @@
 		case MULTIPOINTTYPE:
 		case MULTILINETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			return lwcollection_same((LWCOLLECTION *)lwgeom1,
 				(LWCOLLECTION *)lwgeom2);
@@ -678,6 +723,8 @@
 				dist);
 		case MULTILINETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			return (LWGEOM *)lwcollection_segmentize2d(
 				(LWCOLLECTION *)lwgeom, dist);
@@ -713,6 +760,8 @@
 			return;
 		case MULTILINETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			coll = (LWCOLLECTION *)lwgeom;
 			for (i=0; i<coll->ngeoms; i++)
--- postgis-svn/lwgeom/lwgparse.c	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/lwgparse.c	2007-09-20 16:22:27.000000000 +0200
@@ -140,6 +140,8 @@
 void alloc_multilinestring(void);
 void alloc_multicurve(void);
 void alloc_multipolygon(void);
+void alloc_tin(void);
+void alloc_polyhedralsurface(void);
 void alloc_multisurface(void);
 void alloc_geomertycollection(void);
 void alloc_counter(void);
@@ -562,10 +564,7 @@
         lwnotice("alloc_point");
 #endif
 
-	if( the_geom.lwgi)
-		alloc_stack_tuple(POINTTYPEI,write_type,1);
-	else
-		alloc_stack_tuple(POINTTYPE,write_type,1);
+	alloc_stack_tuple(POINTTYPE,write_type,1);
 
 	minpoints=1;
 	checkclosed=0;
@@ -580,10 +579,7 @@
         lwnotice("alloc_linestring");
 #endif
 
-	if( the_geom.lwgi)
-		alloc_stack_tuple(LINETYPEI,write_type,1);
-	else
-		alloc_stack_tuple(LINETYPE,write_type,1);
+	alloc_stack_tuple(LINETYPE,write_type,1);
 
 	minpoints=2;
 	checkclosed=0;
@@ -634,15 +630,11 @@
         lwnotice("alloc_polygon");
 #endif
 
-	if( the_geom.lwgi)
-		alloc_stack_tuple(POLYGONTYPEI, write_type,1);
-	else
-		alloc_stack_tuple(POLYGONTYPE, write_type,1);
+	alloc_stack_tuple(POLYGONTYPE, write_type,1);
 
 	minpoints=3;
 	checkclosed=1;
         isodd=-1;
-
 }
 
 void
@@ -715,6 +707,29 @@
 }
 
 void
+alloc_tin(void)
+{
+
+#ifdef PGIS_DEBUG_CALLS
+        lwnotice("alloc_tin");
+#endif
+
+        alloc_stack_tuple(TINTYPE,write_type,1);
+}
+
+void
+alloc_polyhedralsurface(void)
+{
+
+#ifdef PGIS_DEBUG_CALLS
+        lwnotice("alloc_polyhedralsurface");
+#endif
+
+        alloc_stack_tuple(POLYHEDRALSURFACETYPE,write_type,1);
+}
+
+
+void
 alloc_multisurface(void)
 {
 
@@ -1021,22 +1036,8 @@
 
 	type &=0x0f;
 
-	if ( the_geom.lwgi  ){
-
-		if ( type<= POLYGONTYPE )
-			alloc_stack_tuple(type +9,write_type,1);
-		else
-			alloc_stack_tuple(type,write_type,1);
-	}
-	else{
-		/* If we are writing lwg and are reading wbki */
-		int4 towrite=type;
-		if (towrite >= POINTTYPEI && towrite <= POLYGONTYPEI){
-			towrite-=9;
-		}
-		alloc_stack_tuple(towrite,write_type,1);
-	}
-
+	alloc_stack_tuple(type,write_type,1);
+	
 	switch(type ){
 		case	POINTTYPE:
 			read_wkb_point(b);
@@ -1066,26 +1067,13 @@
 		case	MULTILINETYPE:
                 case    MULTICURVETYPE:
 		case	MULTIPOLYGONTYPE:
+		case    TINTYPE:
+                case    POLYHEDRALSURFACETYPE:
                 case    MULTISURFACETYPE:
 		case	COLLECTIONTYPE:
 			read_collection(b,parse_wkb);
 			break;
 
-		case	POINTTYPEI:
-			the_geom.from_lwgi=1;
-			read_wkb_point(b);
-			break;
-
-		case	LINETYPEI:
-			the_geom.from_lwgi=1;
-			read_collection(b,read_wkb_point);
-			break;
-
-		case	POLYGONTYPEI:
-			the_geom.from_lwgi=1;
-			read_collection(b,read_collection2);
-			break;
-
 		default:
 			error("Invalid type in wbk");
 	}
--- postgis-svn/lwgeom/lwgeom_geos_c.c	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/lwgeom_geos_c.c	2007-09-18 15:13:41.000000000 +0200
@@ -2935,6 +2935,8 @@
 		case GEOS_MULTIPOINT:
 		case GEOS_MULTILINESTRING:
 		case GEOS_MULTIPOLYGON:
+		case GEOS_TIN:
+		case GEOS_POLYHEDRALSURFACE:
 		case GEOS_GEOMETRYCOLLECTION:
 #ifdef PGIS_DEBUG_GEOS2POSTGIS
 	lwnotice("lwgeom_from_geometry: it's a Collection or Multi");
@@ -3144,6 +3146,8 @@
 		case MULTIPOINTTYPE:
 		case MULTILINETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+                case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			if ( type == MULTIPOINTTYPE )
 				geostype = GEOS_MULTIPOINT;
@@ -3151,6 +3155,10 @@
 				geostype = GEOS_MULTILINESTRING;
 			else if ( type == MULTIPOLYGONTYPE )
 				geostype = GEOS_MULTIPOLYGON;
+			else if ( type == TINTYPE )
+				geostype = GEOS_TIN;
+			else if ( type == POLYHEDRALSURFACETYPE )
+                                geostype = GEOS_POLYHEDRALSURFACE;
 			else
 				geostype = GEOS_GEOMETRYCOLLECTION;
 
--- postgis-svn/lwgeom/lwgeom_jts_wrapper.cpp	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/lwgeom_jts_wrapper.cpp	2007-09-18 15:13:41.000000000 +0200
@@ -86,6 +86,8 @@
 #define	MULTILINETYPE	5
 #define	MULTIPOLYGONTYPE	6
 #define	COLLECTIONTYPE	7
+#define TINTYPE  10
+#define POLYHEDRALSURFACETYPE  11
 
 //###########################################################
 
@@ -924,6 +926,8 @@
 	if ( ! strcmp(type, "MultiLineString") ) return MULTILINETYPE;
 	if ( ! strcmp(type, "MultiPoint") ) return MULTIPOINTTYPE;
 	if ( ! strcmp(type, "MultiPolygon") ) return MULTIPOLYGONTYPE;
+	if ( ! strcmp(type, "Tin") ) return TINTYPE;
+        if ( ! strcmp(type, "PolyhedralSurface") ) return POLYHEDRALSURFACETYPE;
 	if ( ! strcmp(type, "GeometryCollection") ) return COLLECTIONTYPE;
 	else
 	{
@@ -1192,6 +1196,12 @@
 			case MULTIPOLYGONTYPE:
 				g = jtsGeomFactory->createMultiPolygon((JArray<Polygon *>*)subGeoms);
 				break;
+			case TINTYPE:
+                                g = jtsGeomFactory->createTin((JArray<Polygon *>*)subGeoms);
+                                break;
+                        case POLYHEDRALSURFACETYPE:
+                                g = jtsGeomFactory->createPolyhedralSurface((JArray<Polygon *>*)subGeoms);
+                                break;
 			default:
 				NOTICE_MESSAGE("Unsupported type request for PostGIS2JTS_collection");
 				g = NULL;
--- postgis-svn/lwgeom/wktparse.h	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/wktparse.h	2007-09-18 15:35:43.000000000 +0200
@@ -39,14 +39,10 @@
 #define	MULTILINETYPE	5
 #define	MULTIPOLYGONTYPE	6
 #define	COLLECTIONTYPE	7
-
-/* Extended lwgeom integer types */
-#define POINTTYPEI    10
-#define LINETYPEI     11
-#define POLYGONTYPEI  12
-
 #define CURVETYPE       8
 #define COMPOUNDTYPE    9
+#define TINTYPE                 10
+#define POLYHEDRALSURFACETYPE   11
 #define CURVEPOLYTYPE   13
 #define MULTICURVETYPE          14
 #define MULTISURFACETYPE        15
@@ -80,6 +76,8 @@
 void alloc_multilinestring(void);
 void alloc_multicurve(void);
 void alloc_multipolygon(void);
+void alloc_tin(void);
+void alloc_polyhedralsurface(void);
 void alloc_multisurface(void);
 void alloc_geomertycollection(void);
 void alloc_empty();
--- postgis-svn/lwgeom/wktparse.y	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/wktparse.y	2007-09-19 09:32:15.000000000 +0200
@@ -22,8 +22,8 @@
 	const char* wkb;
 }
 
-%token POINT LINESTRING POLYGON MULTIPOINT MULTILINESTRING MULTIPOLYGON GEOMETRYCOLLECTION CIRCULARSTRING COMPOUNDCURVE CURVEPOLYGON MULTICURVE MULTISURFACE
-%token POINTM LINESTRINGM POLYGONM MULTIPOINTM MULTILINESTRINGM MULTIPOLYGONM GEOMETRYCOLLECTIONM CIRCULARSTRINGM COMPOUNDCURVEM CURVEPOLYGONM  MULTICURVEM MULTISURFACEM
+%token POINT LINESTRING POLYGON MULTIPOINT MULTILINESTRING MULTIPOLYGON GEOMETRYCOLLECTION CIRCULARSTRING COMPOUNDCURVE CURVEPOLYGON MULTICURVE MULTISURFACE TIN POLYHEDRALSURFACE
+%token POINTM LINESTRINGM POLYGONM MULTIPOINTM MULTILINESTRINGM MULTIPOLYGONM GEOMETRYCOLLECTIONM CIRCULARSTRINGM COMPOUNDCURVEM CURVEPOLYGONM  MULTICURVEM MULTISURFACEM TINM POLYHEDRALSURFACEM
 %token SRID      
 %token EMPTY
 %token <value> VALUE
@@ -60,6 +60,10 @@
 	|
 	geom_multipolygon
 	|
+	geom_tin
+	|
+	geom_polyhedralsurface
+	|
         geom_multisurface
         |
 	geom_geometrycollection
@@ -145,6 +149,11 @@
 	|
 	linestring_int COMMA a_point;
 
+linestring_t :
+	{alloc_counter();}
+	LPAREN a_point COMMA a_point COMMA a_point COMMA a_point RPAREN 
+	{popc();}
+
 /* CIRCULARSTRING */
 
 geom_circularstring :
@@ -273,6 +282,17 @@
 	|
 	polygon_int COMMA linestring_1
 
+/* TRIANGLE */
+
+nonempty_triangle :
+        { alloc_polygon(); } triangle_1  { pop(); }
+
+triangle_1 :
+        { alloc_counter(); } LPAREN triangle_int RPAREN { pop();}
+
+triangle_int :
+        linestring_t
+        
 /* CURVEPOLYGON */
 
 geom_curvepolygon :
@@ -313,6 +333,48 @@
 	|
 	multipolygon_int COMMA nonempty_polygon
 
+/* TIN */
+
+geom_tin :
+        TIN { alloc_tin(); } tin { pop(); }
+        |
+        TINM { set_zm(0, 1); alloc_tin(); }
+        tin { pop();}
+
+tin :
+        empty
+        |
+        { alloc_counter(); } LPAREN tin_int RPAREN { pop(); }
+
+tin_int :
+        nonempty_triangle
+        |
+        tin_int COMMA nonempty_triangle
+
+/* POLYHEDRALSURFACE */
+
+geom_polyhedralsurface :
+        POLYHEDRALSURFACE { alloc_polyhedralsurface(); } polyhedralsurface { pop(); }
+        |
+        POLYHEDRALSURFACEM { set_zm(0, 1); alloc_polyhedralsurface(); }
+                polyhedralsurface { pop();}
+
+polyhedralsurface :
+        empty
+        |
+        { alloc_counter(); } LPAREN polyhedralsurface_int RPAREN { pop(); }
+
+polyhedralsurface_int :
+        non_empty_polygon_one_ring
+        |
+        polyhedralsurface_int COMMA non_empty_polygon_one_ring
+
+non_empty_polygon_one_ring :
+        { alloc_polygon(); } polygon_one_ring_1  { pop(); }
+
+polygon_one_ring_1 :
+        { alloc_counter(); } LPAREN linestring_1 RPAREN { pop();}
+
 /* MULTISURFACE */
 
 geom_multisurface :
--- postgis-svn/lwgeom/wktparse.lex	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/wktparse.lex	2007-09-18 15:54:52.000000000 +0200
@@ -52,6 +52,10 @@
 <*>MULTICURVEM { return MULTICURVEM; }
 <*>MULTIPOLYGON { return MULTIPOLYGON; }
 <*>MULTIPOLYGONM { return MULTIPOLYGONM; }
+<*>TIN { return TIN; }
+<*>TINM { return TINM; }
+<*>POLYHEDRALSURFACE { return POLYHEDRALSURFACE; }
+<*>POLYHEDRALSURFACEM { return POLYHEDRALSURFACEM; }
 <*>MULTISURFACE { return MULTISURFACE; }
 <*>MULTISURFACEM { return MULTISURFACEM; }
 <*>GEOMETRYCOLLECTION { return GEOMETRYCOLLECTION; }
--- postgis-svn/lwgeom/lwtin.c	1970-01-01 01:00:00.000000000 +0100
+++ postgis-patch/lwgeom/lwtin.c	2007-09-19 14:15:14.000000000 +0200
@@ -0,0 +1,122 @@
+/**********************************************************************
+ * $Id:$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2006 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.
+ * 
+ **********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "liblwgeom.h"
+
+/*#define PGIS_DEBUG_CALLS 1 */
+
+LWTIN *
+lwtin_deserialize(uchar *srl)
+{
+	LWTIN *result;
+	LWGEOM_INSPECTED *insp;
+	int type = lwgeom_getType(srl[0]);
+	int i;
+
+#ifdef PGIS_DEBUG_CALLS
+	lwnotice("lwtin_deserialize called");
+#endif
+
+	if ( type != TINTYPE ) 
+	{
+		lwerror("lwtin_deserialize called on NON tin: %d",
+			type);
+		return NULL;
+	}
+
+	insp = lwgeom_inspect(srl);
+
+	result = lwalloc(sizeof(LWTIN));
+	result->type = insp->type;
+	result->SRID = insp->SRID;
+	result->ngeoms = insp->ngeometries;
+	result->geoms = lwalloc(sizeof(LWPOLY *)*insp->ngeometries);
+
+	if (lwgeom_hasBBOX(srl[0]))
+	{
+		result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
+		memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
+	}
+	else result->bbox = NULL;
+
+	for (i=0; i<insp->ngeometries; i++)
+	{
+		result->geoms[i] = lwpoly_deserialize(insp->sub_geoms[i]);
+		if ( TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type) )
+		{
+			lwerror("Mixed dimensions (multipoly:%d, poly%d:%d)",
+				TYPE_NDIMS(result->type), i,
+				TYPE_NDIMS(result->geoms[i]->type)
+			);
+			return NULL;
+		}
+	}
+
+	return result;
+}
+
+/*
+ * Add 'what' at position 'where'.
+ * where=0 == prepend
+ * where=-1 == append
+ * Returns a MULTIPOLY or a COLLECTION
+ */
+LWGEOM *
+lwtin_add(const LWTIN *to, uint32 where, const LWGEOM *what)
+{
+	LWCOLLECTION *col;
+	LWGEOM **geoms;
+	int newtype;
+	uint32 i;
+
+	if ( where == -1 ) where = to->ngeoms;
+	else if ( where < -1 || where > to->ngeoms )
+	{
+		lwerror("lwtin_add: add position out of range %d..%d",
+			-1, to->ngeoms);
+		return NULL;
+	}
+
+	/* dimensions compatibility are checked by caller */
+
+	/* Construct geoms array */
+	geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
+	for (i=0; i<where; i++)
+	{
+		geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+	}
+	geoms[where] = lwgeom_clone(what);
+	for (i=where; i<to->ngeoms; i++)
+	{
+		geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+	}
+
+	
+	if ( TYPE_GETTYPE(what->type)==POLYGONTYPE){
+		if( lwgeom_as_lwpoly(what)->nrings==1 && 
+		lwgeom_as_lwpoly(what)->rings[0]->npoints==4) newtype = TINTYPE;
+	}
+	else if ( TYPE_GETTYPE(what->type) == POLYGONTYPE) newtype = MULTIPOLYGONTYPE;
+	else newtype = COLLECTIONTYPE;
+	
+	/*newtype = TINTYPE;	*/
+
+	col = lwcollection_construct(newtype,
+		to->SRID, NULL,
+		to->ngeoms+1, geoms);
+	
+	return (LWGEOM *)col;
+
+}
--- postgis-svn/lwgeom/lwpolyhedralsurf.c	1970-01-01 01:00:00.000000000 +0100
+++ postgis-patch/lwgeom/lwpolyhedralsurf.c	2007-09-19 14:15:07.000000000 +0200
@@ -0,0 +1,121 @@
+/**********************************************************************
+ * $Id:$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2006 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.
+ * 
+ **********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "liblwgeom.h"
+
+/*#define PGIS_DEBUG_CALLS 1 */
+
+LWPOLYHEDRALSURFACE *
+lwpolyhedralsurface_deserialize(uchar *srl)
+{
+	LWPOLYHEDRALSURFACE *result;
+	LWGEOM_INSPECTED *insp;
+	int type = lwgeom_getType(srl[0]);
+	int i;
+
+#ifdef PGIS_DEBUG_CALLS
+	lwnotice("lwpolyhedralsurface_deserialize called");
+#endif
+
+	if ( type != POLYHEDRALSURFACETYPE ) 
+	{
+		lwerror("lwpolyhedralsurface_deserialize called on NON tin: %d",
+			type);
+		return NULL;
+	}
+
+	insp = lwgeom_inspect(srl);
+
+	result = lwalloc(sizeof(LWPOLYHEDRALSURFACE));
+	result->type = insp->type;
+	result->SRID = insp->SRID;
+	result->ngeoms = insp->ngeometries;
+	result->geoms = lwalloc(sizeof(LWPOLY *)*insp->ngeometries);
+
+	if (lwgeom_hasBBOX(srl[0]))
+	{
+		result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
+		memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
+	}
+	else result->bbox = NULL;
+
+	for (i=0; i<insp->ngeometries; i++)
+	{
+		result->geoms[i] = lwpoly_deserialize(insp->sub_geoms[i]);
+		if ( TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type) )
+		{
+			lwerror("Mixed dimensions (multipoly:%d, poly%d:%d)",
+				TYPE_NDIMS(result->type), i,
+				TYPE_NDIMS(result->geoms[i]->type)
+			);
+			return NULL;
+		}
+	}
+
+	return result;
+}
+
+/*
+ * Add 'what' at position 'where'.
+ * where=0 == prepend
+ * where=-1 == append
+ * Returns a MULTIPOLY or a COLLECTION
+ */
+LWGEOM *
+lwpolyhedralsurface_add(const LWPOLYHEDRALSURFACE *to, uint32 where, const LWGEOM *what)
+{
+	LWCOLLECTION *col;
+	LWGEOM **geoms;
+	int newtype;
+	uint32 i;
+
+	if ( where == -1 ) where = to->ngeoms;
+	else if ( where < -1 || where > to->ngeoms )
+	{
+		lwerror("lwmline_add: add position out of range %d..%d",
+			-1, to->ngeoms);
+		return NULL;
+	}
+
+	/* dimensions compatibility are checked by caller */
+
+	/* Construct geoms array */
+	geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
+	for (i=0; i<where; i++)
+	{
+		geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+	}
+	geoms[where] = lwgeom_clone(what);
+	for (i=where; i<to->ngeoms; i++)
+	{
+		geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+	}
+
+	
+	if ( TYPE_GETTYPE(what->type)==POLYGONTYPE){
+                if( lwgeom_as_lwpoly(what)->nrings==1) newtype = POLYHEDRALSURFACETYPE;
+	}
+	else if ( TYPE_GETTYPE(what->type) == POLYGONTYPE ) newtype = MULTIPOLYGONTYPE;
+	else newtype = COLLECTIONTYPE;
+	
+	newtype = POLYHEDRALSURFACETYPE;	
+
+	col = lwcollection_construct(newtype,
+		to->SRID, NULL,
+		to->ngeoms+1, geoms);
+	
+	return (LWGEOM *)col;
+
+}
--- postgis-svn/lwgeom/lwgeom_x3d.c	1970-01-01 01:00:00.000000000 +0100
+++ postgis-patch/lwgeom/lwgeom_x3d.c	2007-09-20 16:09:25.000000000 +0200
@@ -0,0 +1,537 @@
+/**********************************************************************
+ * $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.
+ *
+ **********************************************************************
+ *
+ * X3D output routines.
+ *
+ **********************************************************************/
+
+
+#include "postgres.h"
+#include "executor/spi.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+
+Datum LWGEOM_asX3D(PG_FUNCTION_ARGS);
+char *geometry_to_x3d(uchar *srl, char *srs);
+
+static size_t asx3d_point_size(LWPOINT *point);
+static char *asx3d_point(LWPOINT *point);
+static size_t asx3d_line_size(LWLINE *line);
+static char *asx3d_line(LWLINE *line);
+static size_t asx3d_poly_size(LWPOLY *poly);
+static char *asx3d_poly(LWPOLY *poly);
+static size_t asx3d_inspected_size(LWGEOM_INSPECTED *geom);
+
+static char *asx3d_inspected(LWGEOM_INSPECTED *geom);
+static size_t pointArray_X3Dsize(POINTARRAY *pa);
+static size_t pointArray_toX3D(POINTARRAY *pa, char *buf);
+static size_t pointArrayPoly_toX3D(POINTARRAY *pa, char *buf);
+
+
+
+#define DEF_PRECISION 15
+/* Add dot, sign, exponent sign, 'e', exponent digits */
+#define SHOW_DIGS (precision + 10)
+
+/* Globals */
+int precision;
+
+
+/**
+ * Encode feature in X3D 
+ */
+PG_FUNCTION_INFO_V1(LWGEOM_asX3D);
+Datum LWGEOM_asX3D(PG_FUNCTION_ARGS)
+{
+	PG_LWGEOM *geom;
+	char *x3d;
+	text *result;
+	int len;
+		
+	precision = DEF_PRECISION;
+
+	if ( PG_ARGISNULL(0) ) PG_RETURN_NULL();
+
+	geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+	/* Get precision (if provided)  */
+	if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
+			precision = PG_GETARG_INT32(1);
+	
+	if ( precision < 1 || precision > 15 )
+	{
+		elog(ERROR, "Precision out of range 1..15");
+		PG_RETURN_NULL();
+	}
+
+	x3d = geometry_to_x3d(SERIALIZED_FORM(geom), NULL);
+	PG_FREE_IF_COPY(geom, 0);
+
+	len = strlen(x3d) + VARHDRSZ;
+
+	result = palloc(len);
+	SET_VARSIZE(result, len);
+
+	memcpy(VARDATA(result), x3d, len-VARHDRSZ);
+
+	pfree(x3d);
+
+	PG_RETURN_POINTER(result);
+}
+
+/* takes a GEOMETRY and returns a X3D representation */
+char *
+geometry_to_x3d(uchar *geom, char *srs)
+{
+	int type;
+	LWPOINT *point;
+	LWLINE *line;
+	LWPOLY *poly;
+	LWGEOM_INSPECTED *inspected;
+	
+	type = lwgeom_getType(geom[0]);
+	if(!lwgeom_hasZ(geom[0])){
+		elog(ERROR, "asX3D: 'the geometry is not in 3 dimensions !'");
+	}
+
+
+
+	switch (type)
+	{
+
+		case POINTTYPE:
+			point = lwpoint_deserialize(geom);
+			return asx3d_point(point);
+
+		case LINETYPE:
+			line = lwline_deserialize(geom);
+			return asx3d_line(line);
+
+		case POLYGONTYPE:
+			poly = lwpoly_deserialize(geom);
+			return asx3d_poly(poly);
+
+		default:
+			inspected = lwgeom_inspect(geom);
+			return asx3d_inspected(inspected);
+	}
+}
+
+static size_t
+asx3d_point_size(LWPOINT *point)
+{
+	int size;
+	size = pointArray_X3Dsize(point->point);
+	size += sizeof("<Coordinate point''/");
+	return size;
+}
+
+static size_t
+asx3d_point_buf(LWPOINT *point, char *output)
+{
+	char *ptr = output;
+	ptr += sprintf(ptr, "<Coordinate point=\"");
+	ptr += pointArray_toX3D(point->point, ptr);
+	ptr += sprintf(ptr, "\"/>\n");
+
+	return (ptr-output);
+}
+
+static char *
+asx3d_point(LWPOINT *point)
+{
+	char *output;
+	int size;
+	
+	size = asx3d_point_size(point);
+	output = palloc(size);
+	asx3d_point_buf(point, output);
+	return output;
+}
+
+static size_t
+asx3d_line_size(LWLINE *line )
+{
+	int size;
+	size = pointArray_X3Dsize(line->points);
+	size += sizeof("<LineSet vertexCount=''><Coordinate point=''/></LineSet>");
+	return size;
+}
+
+static size_t
+asx3d_line_buf(LWLINE *line, char *output)
+{
+	char *ptr=output;
+	int vcount = (line->points)->npoints;
+	ptr += sprintf(ptr, "<LineSet vertexCount=\"%d\">\n",vcount);
+	ptr += sprintf(ptr, "<Coordinate point=\"");
+	ptr += pointArray_toX3D(line->points, ptr);
+	ptr += sprintf(ptr,"\"/>\n");
+	ptr += sprintf(ptr, "</LineSet>\n");
+
+	return (ptr-output);
+}
+
+static char *
+asx3d_line(LWLINE *line)
+{
+	char *output;
+	int size;
+
+	size = asx3d_line_size(line);
+	output = palloc(size);
+	asx3d_line_buf(line, output);
+	return output;
+}
+
+static size_t
+asx3d_poly_size(LWPOLY *poly)
+{
+	size_t size;
+	int i;
+	if(poly->nrings>1){
+                        elog(ERROR, "asX3D: in X3D, polygons must have just one ring.");
+        }
+	size += sizeof("<IndexedFaceSet coordIndex=''");
+	size += 10*poly->rings[0]->npoints + poly->rings[0]->npoints -1;
+	size += sizeof("<Coordinate point=''/></IndexedFaceSet");
+	size += pointArray_X3Dsize(poly->rings[0]);
+
+	return size;
+}
+
+static size_t
+asx3d_poly_buf(LWPOLY *poly, char *output)
+{
+	int i;
+	char *ptr=output;
+	
+	ptr += sprintf(ptr, "<IndexedFaceSet coordIndex=\"");
+	for(i = 0;i<poly->rings[0]->npoints - 1 ;i++){
+		if(i)ptr += sprintf(ptr,",");
+		ptr += sprintf(ptr,"%d", i);
+	}
+	ptr += sprintf(ptr,"\">\n");
+	ptr += sprintf(ptr,"<Coordinate point=\"");
+	ptr += pointArrayPoly_toX3D(poly->rings[0], ptr);
+	ptr += sprintf(ptr,"\"/>\n");
+	ptr += sprintf(ptr,"</IndexedFaceSet>\n");
+
+	return (ptr-output);
+}
+
+static char *
+asx3d_poly(LWPOLY *poly)
+{
+	if(poly->nrings>1){
+		elog(ERROR, "asX3D: in X3D, polygons must have just one ring ");
+	}
+	char *output;
+	int size;
+
+	size = asx3d_poly_size(poly);
+	output = palloc(size);
+	asx3d_poly_buf(poly, output);
+	return output;
+}
+
+/*
+ * Compute max size required for X3D version of this 
+ * inspected geometry. Will recurse when needed.
+ * Don't call this with single-geoms inspected.
+ */
+static size_t
+asx3d_inspected_size(LWGEOM_INSPECTED *insp)
+{
+	int i;
+	int j;
+	size_t size;
+
+	size = sizeof("");
+
+	for (i=0; i<insp->ngeometries; i++)
+	{
+		LWPOINT *point;
+		LWLINE *line;
+		LWPOLY *poly;
+		LWGEOM_INSPECTED *subinsp;
+		uchar *subgeom;
+
+		if (lwgeom_getType(insp->type)==MULTIPOINTTYPE){
+			size += sizeof("<Coordinate point''/");
+			return size;
+		}
+		if (lwgeom_getType(insp->type)==MULTILINETYPE){
+			size += sizeof("<LineSet vertexCount=''><Coordinate point=\"\"/></LineSet>");
+			return size;
+		}
+		if (lwgeom_getType(insp->type)==MULTIPOLYGONTYPE || 
+		lwgeom_getType(insp->type)==POLYHEDRALSURFACETYPE ||
+                lwgeom_getType(insp->type)==TINTYPE){
+			int nbpts=0;
+			for(j=0; j<insp->ngeometries; j++)
+			{
+				if (lwgeom_getpoly_inspected(insp, i)->nrings>1){
+				elog(ERROR, "asX3D: in X3D, polygons must have just one ring.");
+				}
+				else
+				{
+				nbpts +=lwgeom_getpoly_inspected(insp, j)->rings[0]->npoints;
+				size += pointArray_X3Dsize(lwgeom_getpoly_inspected(insp, j)->rings[0]);
+				}
+			}
+			size += sizeof("<IndexedFaceSet coordIndex=''");
+		        size += (nbpts - insp->ngeometries)*10 +
+					2*(insp->ngeometries - 1) +nbpts -2;
+                        
+		        size += sizeof("<Coordinate point=\"\"/></IndexedFaceSet");
+			
+			return size;
+		}
+		if ((point=lwgeom_getpoint_inspected(insp, i)))
+		{
+			size += pointArray_X3Dsize(point->point);
+			pfree_point(point);
+			return size;
+		}
+		else if ((line=lwgeom_getline_inspected(insp, i)))
+		{
+			size +=pointArray_X3Dsize(line->points);
+			pfree_line(line);
+			return size;
+		}
+		else if ((poly=lwgeom_getpoly_inspected(insp, i)))
+		{
+			for (i=0; i<poly->nrings; i++)
+                	size += pointArray_X3Dsize(poly->rings[i]);
+			pfree_polygon(poly);
+			return size;
+		}
+		else if (lwgeom_getType(insp->type)==COLLECTIONTYPE)
+		{
+			subgeom = lwgeom_getsubgeometry_inspected(insp, i);
+			subinsp = lwgeom_inspect(subgeom);
+			size += asx3d_inspected_size(subinsp);
+			pfree_inspected(subinsp);
+			return size;
+		}
+		else
+		{
+			elog(ERROR, 
+			"asX3D: this type of geometry is not supported by the function asX3D.");
+		}
+	}
+
+	return size;
+}
+
+/*
+ * Don't call this with single-geoms inspected!
+ */
+static size_t
+asx3d_inspected_buf(LWGEOM_INSPECTED *insp, char *output)
+{
+	int i;
+	int j;
+	char *ptr;
+	ptr = output;
+
+	if(lwgeom_getType(insp->type)==MULTIPOINTTYPE)
+	{
+		ptr += sprintf(ptr, "<Coordinate point=\"");
+		for (i=0; i<insp->ngeometries; i++)
+		{
+			if(i)
+			{
+				ptr+=sprintf(ptr, ",");
+			}
+			ptr += pointArray_toX3D(lwgeom_getpoint_inspected(insp,i)->point, ptr);
+		}
+		ptr += sprintf(ptr, "\"/>\n");
+		return (ptr-output);
+	}
+	if (lwgeom_getType(insp->type)==MULTILINETYPE)
+	{
+		ptr += sprintf(ptr, "<LineSet vertexCount=\"");
+		for (i=0; i<insp->ngeometries; i++)
+		{
+			if (i)
+			{
+				ptr += sprintf(ptr," ");
+			}
+			int n = lwgeom_getline_inspected(insp,i)->points->npoints;
+			ptr += sprintf(ptr, "%d",n);
+		}
+		ptr += sprintf(ptr, "\">\n");
+		ptr += sprintf(ptr, "<Coordinate point=\"");
+		for (i=0; i<insp->ngeometries; i++)
+		{
+	       	        if(i)
+			{
+				ptr+=sprintf(ptr, ",");
+			}
+	                ptr += pointArray_toX3D(lwgeom_getline_inspected(insp,i)->points, ptr);
+       	        }
+        	ptr += sprintf(ptr, "\"/>\n");
+		ptr += sprintf(ptr, "</LineSet>\n");
+		return (ptr-output);
+	}
+
+	if (lwgeom_getType(insp->type)==MULTIPOLYGONTYPE ||
+	lwgeom_getType(insp->type)==POLYHEDRALSURFACETYPE ||
+        lwgeom_getType(insp->type)==TINTYPE){
+		ptr += sprintf(ptr, "<IndexedFaceSet coordIndex=\"");
+		int ind=0;
+		for (i=0; i<insp->ngeometries; i++)
+		{
+			if (i)
+			{
+				ptr += sprintf(ptr, ",-1");
+			}
+			for(j=0; j<lwgeom_getpoly_inspected(insp,i)->rings[0]->npoints-1; j++)
+			{
+				if(ind)
+				{
+					ptr += sprintf(ptr, ",");
+				}
+				ptr += sprintf(ptr, "%d",ind);
+				ind++;
+			}
+		}
+		ptr += sprintf(ptr, "\">\n");
+		ptr += sprintf(ptr, "<Coordinate point=\"");
+		for (i=0; i<insp->ngeometries; i++)
+	        {
+			if(i){ptr+=sprintf(ptr, ",");}
+			ptr += pointArrayPoly_toX3D(lwgeom_getpoly_inspected(insp,i)->rings[0],
+			ptr);
+		}
+		ptr += sprintf(ptr, "\"/>\n");
+	        ptr += sprintf(ptr, "</IndexedFaceSet>\n");
+		return (ptr-output);
+	}
+	else
+	{	
+		/*type = GEOMETRYCOLLECTION */
+		/* It is decomposed in several geometries*/
+		for (i=0; i<insp->ngeometries; i++)
+		{
+			LWPOINT *point;
+			LWLINE *line;
+			LWPOLY *poly;
+			LWGEOM_INSPECTED *subinsp;
+			uchar *subgeom;
+				
+			if ((point=lwgeom_getpoint_inspected(insp, i)))
+			{
+				ptr += asx3d_point_buf(point, ptr);
+				pfree_point(point);
+			}
+			else if ((line=lwgeom_getline_inspected(insp, i)))
+			{
+				ptr += asx3d_line_buf(line, ptr);
+				pfree_line(line);
+			}
+			else if ((poly=lwgeom_getpoly_inspected(insp, i)))
+			{
+				ptr += asx3d_poly_buf(poly, ptr);
+				pfree_polygon(poly);
+			}
+			else
+			{
+				subgeom = lwgeom_getsubgeometry_inspected(insp, i);
+				subinsp = lwgeom_inspect(subgeom);
+				ptr += asx3d_inspected_buf(subinsp, ptr);
+				pfree_inspected(subinsp);
+			}
+		}
+	}
+	return (ptr-output);
+}
+
+/*
+ * Don't call this with single-geoms inspected!
+ */
+static char *
+asx3d_inspected(LWGEOM_INSPECTED *insp)
+{
+	char *x3d;
+	size_t size;
+	size = asx3d_inspected_size(insp);
+	x3d = palloc(size);
+	asx3d_inspected_buf(insp, x3d);
+	return x3d;
+}
+
+
+/*
+ * Returns maximum size of rendered pointarray in bytes.
+ */
+static size_t
+pointArray_X3Dsize(POINTARRAY *pa)
+{
+	return TYPE_NDIMS(pa->dims) * pa->npoints * (SHOW_DIGS+(TYPE_NDIMS(pa->dims)-1));
+}
+
+static size_t
+pointArray_toX3D(POINTARRAY *pa, char *output)
+{
+	int i;
+	char *ptr;
+
+	ptr = output;
+	{
+		for (i=0; i<pa->npoints; i++)
+		{
+			POINT4D pt;
+			getPoint4d_p(pa, i, &pt);
+			if ( i ) ptr += sprintf(ptr, ",");
+			ptr += sprintf(ptr, "%.*g %.*g %.*g",
+				precision, pt.x,
+				precision, pt.y,
+				precision, pt.z);
+			
+		}
+	}
+
+	return ptr-output;
+}
+
+/*
+ * Same function but called when type is POLYGON
+ */
+
+static size_t
+pointArrayPoly_toX3D(POINTARRAY *pa, char *output)
+{
+        int i;
+        char *ptr;
+
+        ptr = output;
+        {
+                for (i=0; i<pa->npoints-1; i++)
+                {
+                        POINT4D pt;
+                        getPoint4d_p(pa, i, &pt);
+                        if ( i ) ptr += sprintf(ptr, ",");
+                        ptr += sprintf(ptr, "%.*g %.*g %.*g",
+                                precision, pt.x,
+                                precision, pt.y,
+                                precision, pt.z);
+
+                }
+        }
+
+        return ptr-output;
+}
+
--- postgis-svn/lwgeom/wktunparse.c	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/wktunparse.c	2007-09-18 15:33:40.000000000 +0200
@@ -227,13 +227,7 @@
 	
 	if ( type  == POINTTYPE )
 		return output_point(++geom,suppress);
-	else if ( type == POINTTYPEI ){
-		lwgi++;
-		geom=output_point(++geom,0);
-		lwgi--;
-		return geom;
-	}
-	
+		
 	return output_wkt(geom,suppress);
 }
 
@@ -391,7 +385,23 @@
 			}
 			geom = output_collection(geom,output_wkt,2);
 			break;
-                case MULTISURFACETYPE:
+                case TINTYPE:
+                        if ( supress < 2 )
+                        {
+                                if (writeM) write_str("TINM");
+                                else write_str("TIN");
+                        }
+                        geom = output_collection(geom,output_wkt,2);
+                        break;
+		case POLYHEDRALSURFACETYPE:
+                        if ( supress < 2 )
+                        {
+                                if (writeM) write_str("POLYHEDRALSURFACEM");
+                                else write_str("POLYHEDRALSURFACE");
+                        }
+                        geom = output_collection(geom,output_wkt,2);
+                        break;
+		case MULTISURFACETYPE:
                         if ( supress < 2)
                         {
                                 if (writeM) write_str("MULTISURFACEM");
@@ -408,36 +418,6 @@
 			geom = output_collection(geom,output_wkt,1);
 			break;
 
-		case POINTTYPEI:
-			if ( supress < 2 )
-			{
-				if (writeM) write_str("POINTM");
-				else write_str("POINT");
-			}
-			lwgi++;
-			geom=output_single(geom,0);
-			lwgi--;
-			break;
-		case LINETYPEI:
-			if ( supress < 2 )
-			{
-				if (writeM) write_str("LINESTRINGM");
-				else write_str("LINESTRING");
-			}
-			lwgi++;
-			geom = output_collection(geom,output_point,0);
-			lwgi--;
-			break;
-		case POLYGONTYPEI:
-			if ( supress < 2 )
-			{
-				if (writeM) write_str("POLYGONM");
-				else write_str("POLYGON");
-			}
-			lwgi++;
-			geom =output_collection(geom,output_collection_2,0);
-			lwgi--;
-			break;
 	}
 	return geom;
 }
@@ -635,29 +615,6 @@
 			geom = output_wkb_collection(geom,output_wkb);
 			break;
 
-		/*
-			These don't output standard wkb at the moment
-			the output and integer version.
-
-			however you could run it through the wkt parser
-			for a lwg and then output that.  There should
-			also be a force_to_real_(lwgi)
-		*/
-		case POINTTYPEI:
-			lwgi++;
-			geom=output_wkb_point(geom);
-			lwgi--;
-			break;
-		case LINETYPEI:
-			lwgi++;
-			geom = output_wkb_collection(geom,output_wkb_point);
-			lwgi--;
-			break;
-		case POLYGONTYPEI:
-			lwgi++;
-			geom = output_wkb_collection(geom,output_wkb_collection_2);
-			lwgi--;
-			break;
 	}
 	return geom;
 }
--- postgis-svn/lwgeom/lwpostgis.sql.in	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/lwpostgis.sql.in	2007-09-18 15:13:41.000000000 +0200
@@ -4720,6 +4720,48 @@
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
 -----------------------------------------------------------------------
+-- X3D OUTPUT
+-----------------------------------------------------------------------
+-- AsX3D(geom, precision, version)
+-- Deprecation in 1.2.3
+CREATEFUNCTION AsX3D(geometry, int4, int4)
+        RETURNS TEXT
+        AS '@MODULE_FILENAME@','LWGEOM_asX3D'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- Availability: 1.2.2
+CREATEFUNCTION ST_AsX3D(geometry, int4, int4)
+        RETURNS TEXT
+        AS '@MODULE_FILENAME@','LWGEOM_asX3D'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsX3D(geom, precision) / version=2
+-- Deprecation in 1.2.3
+CREATEFUNCTION AsX3D(geometry, int4)
+        RETURNS TEXT
+        AS '@MODULE_FILENAME@','LWGEOM_asX3D'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- Availability: 1.2.2
+CREATEFUNCTION ST_AsX3D(geometry, int4)
+        RETURNS TEXT
+        AS '@MODULE_FILENAME@','LWGEOM_asX3D'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsX3D(geom) / precision=15 version=2
+-- Deprecation in 1.2.3
+CREATEFUNCTION AsX3D(geometry)
+        RETURNS TEXT
+        AS '@MODULE_FILENAME@','LWGEOM_asX3D'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- Availabiltiy: 1.2.2
+CREATEFUNCTION ST_AsX3D(geometry)
+        RETURNS TEXT
+        AS '@MODULE_FILENAME@','LWGEOM_asX3D'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-----------------------------------------------------------------------
 -- KML OUTPUT
 -----------------------------------------------------------------------
 -- AsUKML(geom, precision, version)
--- postgis-svn/lwgeom/lwgeom_debug.c	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/lwgeom_debug.c	2007-09-19 14:07:30.000000000 +0200
@@ -28,6 +28,8 @@
 		case MULTIPOINTTYPE:
 		case MULTILINETYPE:
 		case MULTIPOLYGONTYPE:
+		case TINTYPE:
+		case POLYHEDRALSURFACETYPE:
 		case COLLECTIONTYPE:
 			return lwcollection_summary((LWCOLLECTION *)lwgeom, offset);
 		default:
--- postgis-svn/lwgeom/Makefile	2007-09-20 16:22:47.000000000 +0200
+++ postgis-patch/lwgeom/Makefile	2007-09-18 15:13:41.000000000 +0200
@@ -74,9 +74,9 @@
 override CFLAGS += $(CSTAR_FLAGS)
 override CXXFLAGS += $(CSTAR_FLAGS)
 
-SA_OBJS=measures.o box2d.o ptarray.o lwgeom_api.o lwgeom.o lwpoint.o lwline.o lwpoly.o lwmpoint.o lwmline.o lwmpoly.o lwcollection.o $(GEOS_WRAPPER) $(JTS_WRAPPER) wktunparse.o lwgparse.o wktparse.tab.o lex.yy.o vsprintf.o
+SA_OBJS=measures.o box2d.o ptarray.o lwgeom_api.o lwgeom.o lwpoint.o lwline.o lwpoly.o lwmpoint.o lwmline.o lwmpoly.o lwpolyhedralsurf.o lwtin.o lwcollection.o $(GEOS_WRAPPER) $(JTS_WRAPPER) wktunparse.o lwgparse.o wktparse.tab.o lex.yy.o vsprintf.o
 
-OBJS=$(SA_OBJS) liblwgeom.o lwgeom_pg.o lwgeom_debug.o lwgeom_spheroid.o lwgeom_ogc.o lwgeom_functions_analytic.o $(JTS_OBJ) lwgeom_inout.o lwgeom_estimate.o lwgeom_functions_basic.o lwgeom_gist.o lwgeom_btree.o lwgeom_transform.o stringBuffer.o lwgeom_box.o lwgeom_box3d.o lwgeom_box2dfloat4.o lwgeom_chip.o lwgeom_svg.o lwgeom_gml.o lwgeom_kml.o lwgeom_triggers.o lwgeom_dump.o lwgeom_functions_lrs.o long_xact.o lwcurve.o lwcompound.o lwcurvepoly.o lwmcurve.o lwmsurface.o lwgeom_sqlmm.o lwgeom_rtree.o
+OBJS=$(SA_OBJS) liblwgeom.o lwgeom_pg.o lwgeom_debug.o lwgeom_spheroid.o lwgeom_ogc.o lwgeom_functions_analytic.o $(JTS_OBJ) lwgeom_inout.o lwgeom_estimate.o lwgeom_functions_basic.o lwgeom_gist.o lwgeom_btree.o lwgeom_transform.o stringBuffer.o lwgeom_box.o lwgeom_box3d.o lwgeom_box2dfloat4.o lwgeom_chip.o lwgeom_svg.o lwgeom_gml.o lwgeom_kml.o lwgeom_x3d.o lwgeom_triggers.o lwgeom_dump.o lwgeom_functions_lrs.o long_xact.o lwcurve.o lwcompound.o lwcurvepoly.o lwmcurve.o lwmsurface.o lwgeom_sqlmm.o lwgeom_rtree.o
 
 #OTHERS=y.output lex.yy.c wktparse.tab.c wktparse.tab.h lwpostgis.sql
 OTHERS=y.output postgis_geos_version.h
-------------- next part --------------
--- geos-svn/swig/geos.i.in	2007-09-24 16:30:27.000000000 +0200
+++ geos-patch/trunk/swig/geos.i.in	2007-09-24 16:50:49.000000000 +0200
@@ -49,6 +49,8 @@
     GEOS_MULTIPOINT,
     GEOS_MULTILINESTRING,
     GEOS_MULTIPOLYGON,
+    GEOS_TIN,
+    GEOS_POLYHEDRALSURFACE,
     GEOS_GEOMETRYCOLLECTION
 };
 
@@ -265,6 +267,8 @@
 %rename(MultiLineString) GeosMultiLineString;
 %rename(MultiLinearRing) GeosMultiLinearRing;
 %rename(MultiPolygon) GeosMultiPolygon;
+%rename(Tin) GeosTin;
+%rename(PolyhedralSurface) GeosPolyhedralSurface;
 %rename(WktReader) GeosWktReader;
 %rename(WktWriter) GeosWktWriter;
 %rename(WkbReader) GeosWkbReader;
@@ -284,6 +288,8 @@
 typedef void GeosMultiLineString;
 typedef void GeosMultiLinearRing;
 typedef void GeosMultiPolygon;
+typedef void GeosTin;
+typedef void GeosPolyhedralSurface;
 
 typedef void GeosWktReader;
 typedef void GeosWktWriter;
@@ -339,6 +345,12 @@
 	case GEOS_MULTIPOLYGON:
         $result = SWIG_NewPointerObj(SWIG_as_voidptr(result), $descriptor(GeosMultiPolygon*), 0 | $owner);
         break;
+	case GEOS_TIN:
+        $result = SWIG_NewPointerObj(SWIG_as_voidptr(result), $descriptor(GeosTin*), 0 | $owner);
+        break;
+	case GEOS_POLYHEDRALSURFACE:
+        $result = SWIG_NewPointerObj(SWIG_as_voidptr(result), $descriptor(GeosPolyhedralSurface*), 0 | $owner);
+        break;
 	case GEOS_GEOMETRYCOLLECTION:
         $result = SWIG_NewPointerObj(SWIG_as_voidptr(result), $descriptor(GeosGeometryCollection*), 0 | $owner);
         break;
@@ -875,6 +887,32 @@
 }
 };
 
+class GeosTin: public GeosGeometryCollection
+{
+public:
+%extend
+{
+    ~GeosTin()
+    {
+        GEOSGeom geom = (GEOSGeom) self;
+        GEOSGeom_destroy(geom);
+    }
+}
+};
+
+class GeosPolyhedralSurface: public GeosGeometryCollection
+{
+public:
+%extend
+{
+    ~GeosPolyhedralSurface()
+    {
+        GEOSGeom geom = (GEOSGeom) self;
+        GEOSGeom_destroy(geom);
+    }
+}
+};
+
 
 // ==== Geometry Constructors ===========
 %newobject createPoint;
--- geos-svn/source/geom/PolyhedralSurface.cpp	1970-01-01 01:00:00.000000000 +0100
+++ geos-patch/trunk/source/geom/PolyhedralSurface.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -0,0 +1,107 @@
+/**********************************************************************
+ * $Id:$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2001-2002 Vivid Solutions Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/geom/Geometry.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/PolyhedralSurface.h>
+#include <geos/geom/MultiLineString.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/Dimension.h>
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+#ifndef GEOS_INLINE
+# include "geos/geom/PolyhedralSurface.inl"
+#endif
+
+using namespace std;
+
+namespace geos {
+namespace geom { // geos::geom
+
+/*protected*/
+PolyhedralSurface::PolyhedralSurface(vector<Geometry *> *newPolys, const GeometryFactory *factory)
+	: GeometryCollection(newPolys,factory)
+{}
+
+PolyhedralSurface::~PolyhedralSurface(){}
+
+Dimension::DimensionType
+PolyhedralSurface::getDimension() const {
+	return Dimension::A; // area
+}
+
+int PolyhedralSurface::getBoundaryDimension() const {
+	return 1;
+}
+
+string PolyhedralSurface::getGeometryType() const {
+	return "PolyhedralSurface";
+}
+
+bool PolyhedralSurface::isSimple() const {
+	return true;
+}
+
+Geometry* PolyhedralSurface::getBoundary() const {
+	if (isEmpty()) {
+		return getFactory()->createGeometryCollection(NULL);
+	}
+	vector<Geometry *>* allRings=new vector<Geometry *>();
+	for (size_t i = 0; i < geometries->size(); i++) {
+		Polygon *pg=dynamic_cast<Polygon *>((*geometries)[i]);
+		assert(pg);
+		Geometry *g=pg->getBoundary();
+		LineString *ls=dynamic_cast<LineString *>(g);
+		if ( ls )
+		{
+			allRings->push_back(ls);
+		}
+		else
+		{
+			GeometryCollection* rings=(GeometryCollection*)g;
+			for (size_t j=0, jn=rings->getNumGeometries();
+					j<jn; ++j)
+			{
+				allRings->push_back(rings->getGeometryN(j)->clone());
+			}
+			delete g;
+		}
+	}
+
+	Geometry *ret=getFactory()->createMultiLineString(allRings);
+	return ret;
+}
+
+bool
+PolyhedralSurface::equalsExact(const Geometry *other, double tolerance) const
+{
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+	return GeometryCollection::equalsExact(other, tolerance);
+}
+GeometryTypeId
+PolyhedralSurface::getGeometryTypeId() const {
+	return GEOS_POLYHEDRALSURFACE;
+}
+
+} // namespace geos::geom
+} // namespace geos
+
+
--- geos-svn/source/geom/Tin.cpp	1970-01-01 01:00:00.000000000 +0100
+++ geos-patch/trunk/source/geom/Tin.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -0,0 +1,107 @@
+/**********************************************************************
+ * $Id:$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2001-2002 Vivid Solutions Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/geom/Geometry.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/MultiLineString.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/Dimension.h>
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+#ifndef GEOS_INLINE
+# include "geos/geom/Tin.inl"
+#endif
+
+using namespace std;
+
+namespace geos {
+namespace geom { // geos::geom
+
+/*protected*/
+Tin::Tin(vector<Geometry *> *newPolys, const GeometryFactory *factory)
+	: GeometryCollection(newPolys,factory)
+{} 
+
+Tin::~Tin(){}
+
+Dimension::DimensionType
+Tin::getDimension() const {
+	return Dimension::A; // area
+}
+
+int Tin::getBoundaryDimension() const {
+	return 1;
+}
+
+string Tin::getGeometryType() const {
+	return "Tin";
+}
+
+bool Tin::isSimple() const {
+	return true;
+}
+
+Geometry* Tin::getBoundary() const {
+	if (isEmpty()) {
+		return getFactory()->createGeometryCollection(NULL);
+	}
+	vector<Geometry *>* allRings=new vector<Geometry *>();
+	for (size_t i = 0; i < geometries->size(); i++) {
+		Polygon *pg=dynamic_cast<Polygon *>((*geometries)[i]);
+		assert(pg);
+		Geometry *g=pg->getBoundary();
+		LineString *ls=dynamic_cast<LineString *>(g);
+		if ( ls )
+		{
+			allRings->push_back(ls);
+		}
+		else
+		{
+			GeometryCollection* rings=(GeometryCollection*)g;
+			for (size_t j=0, jn=rings->getNumGeometries();
+					j<jn; ++j)
+			{
+				allRings->push_back(rings->getGeometryN(j)->clone());
+			}
+			delete g;
+		}
+	}
+
+	Geometry *ret=getFactory()->createMultiLineString(allRings);
+	return ret;
+}
+
+bool
+Tin::equalsExact(const Geometry *other, double tolerance) const
+{
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+	return GeometryCollection::equalsExact(other, tolerance);
+}
+GeometryTypeId
+Tin::getGeometryTypeId() const {
+	return GEOS_TIN;
+}
+
+} // namespace geos::geom
+} // namespace geos
+
+
--- geos-svn/source/geom/util/GeometryEditor.cpp	2007-09-24 16:30:19.000000000 +0200
+++ geos-patch/trunk/source/geom/util/GeometryEditor.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -18,6 +18,8 @@
 #include <geos/geom/Geometry.h>
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/Polygon.h>
@@ -74,6 +76,8 @@
 	if ((typeid(*geometry)==typeid(GeometryCollection)) ||
 				(typeid(*geometry)==typeid(MultiPoint)) ||
 				(typeid(*geometry)==typeid(MultiPolygon)) ||
+				(typeid(*geometry)==typeid(Tin)) ||
+				(typeid(*geometry)==typeid(PolyhedralSurface)) ||
 				(typeid(*geometry)==typeid(MultiLineString))) {
 		return editGeometryCollection((const GeometryCollection*) geometry, operation);
 	}
@@ -158,6 +162,14 @@
 		delete newCollection;
 		return factory->createMultiPolygon(geometries);
 	}
+	else if (typeid(*newCollection)==typeid(Tin)) {
+                delete newCollection;
+                return factory->createTin(geometries);
+        }
+	else if (typeid(*newCollection)==typeid(PolyhedralSurface)) {
+                delete newCollection;
+                return factory->createPolyhedralSurface(geometries);
+        }
 	else {
 		delete newCollection;
 		return factory->createGeometryCollection(geometries);
--- geos-svn/source/geom/Makefile.am	2007-09-24 16:30:19.000000000 +0200
+++ geos-patch/trunk/source/geom/Makefile.am	2007-09-24 16:50:49.000000000 +0200
@@ -29,6 +29,8 @@
 	MultiLineString.cpp \
 	MultiPoint.cpp \
 	MultiPolygon.cpp \
+	Tin.cpp	\
+	PolyhedralSurface.cpp	\
 	Point.cpp \
 	Polygon.cpp \
 	PrecisionModel.cpp \
--- geos-svn/source/geom/GeometryFactory.cpp	2007-09-24 16:30:19.000000000 +0200
+++ geos-patch/trunk/source/geom/GeometryFactory.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -29,6 +29,8 @@
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/GeometryCollection.h>
 #include <geos/geom/PrecisionModel.h>
 #include <geos/geom/Envelope.h>
@@ -384,6 +386,78 @@
 }
 
 /*public*/
+Tin*
+GeometryFactory::createTin() const
+{
+	return new Tin(NULL,this);
+}
+
+/*public*/
+Tin*
+GeometryFactory::createTin(vector<Geometry *> *newPolys) const
+{
+	return new Tin(newPolys,this);
+}
+
+/*public*/
+Tin*
+GeometryFactory::createTin(const vector<Geometry *> &fromPolys) const
+{
+	vector<Geometry *>*newGeoms = new vector<Geometry *>(fromPolys.size());
+	for (size_t i=0; i<fromPolys.size(); i++)
+	{
+		(*newGeoms)[i] = fromPolys[i]->clone();
+	}
+	Tin *g = NULL;
+	try {
+		g = new Tin(newGeoms,this);
+	} catch (...) {
+		for (size_t i=0; i<newGeoms->size(); i++) {
+			delete (*newGeoms)[i];
+		}
+		delete newGeoms;
+		throw;
+	}
+	return g;
+}
+
+/*public*/
+PolyhedralSurface*
+GeometryFactory::createPolyhedralSurface() const
+{
+	return new PolyhedralSurface(NULL,this);
+}
+
+/*public*/
+PolyhedralSurface*
+GeometryFactory::createPolyhedralSurface(vector<Geometry *> *newPolys) const
+{
+	return new PolyhedralSurface(newPolys,this);
+}
+
+/*public*/
+PolyhedralSurface*
+GeometryFactory::createPolyhedralSurface(const vector<Geometry *> &fromPolys) const
+{
+	vector<Geometry *>*newGeoms = new vector<Geometry *>(fromPolys.size());
+	for (size_t i=0; i<fromPolys.size(); i++)
+	{
+		(*newGeoms)[i] = fromPolys[i]->clone();
+	}
+	PolyhedralSurface *g = NULL;
+	try {
+		g = new PolyhedralSurface(newGeoms,this);
+	} catch (...) {
+		for (size_t i=0; i<newGeoms->size(); i++) {
+			delete (*newGeoms)[i];
+		}
+		delete newGeoms;
+		throw;
+	}
+	return g;
+}
+
+/*public*/
 LinearRing*
 GeometryFactory::createLinearRing() const
 {
--- geos-svn/source/operation/valid/IsValidOp.cpp	2007-09-24 16:30:19.000000000 +0200
+++ geos-patch/trunk/source/operation/valid/IsValidOp.cpp	2007-09-24 17:44:15.000000000 +0200
@@ -35,6 +35,8 @@
 #include <geos/geom/Point.h>
 #include <geos/geom/Polygon.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/GeometryCollection.h>
 
 #include <typeinfo>
@@ -123,6 +125,8 @@
 	else if (typeid(*g)==typeid(LineString)) checkValid((LineString*)g);
 	else if (typeid(*g)==typeid(Polygon)) checkValid((Polygon*)g);
 	else if (typeid(*g)==typeid(MultiPolygon)) checkValid((MultiPolygon*)g);
+	else if (typeid(*g)==typeid(Tin)) checkValid((Tin*)g);
+	else if (typeid(*g)==typeid(PolyhedralSurface)) checkValid((PolyhedralSurface*)g);
 	else if ((gc=dynamic_cast<const GeometryCollection *>(g)))
 		checkValid(gc);
 	else throw util::UnsupportedOperationException();
@@ -259,6 +263,81 @@
 }
 
 void
+IsValidOp::checkValid(const Tin *g)
+{
+
+	unsigned int ngeoms = g->getNumGeometries();
+	vector<const Polygon *>polys(ngeoms);	
+
+	for (unsigned int i=0; i<ngeoms; ++i)
+	{
+		const Polygon *p = (const Polygon *)g->getGeometryN(i);
+
+		if (!p->isSimple())
+                {
+                validErr = new TopologyValidationError(
+                        TopologyValidationError::eNotOneRing);
+		}
+		checkInvalidCoordinates(p);
+		if (validErr != NULL) return;
+
+		checkClosedRings(p);
+		if (validErr != NULL) return;
+
+		polys[i]=p;
+	}
+
+	GeometryGraph graph(0,g);
+
+	checkConsistentArea3DSurf(&graph);
+	if (validErr!=NULL) return;
+
+	checkWellConnected(g);
+        if (validErr!=NULL) return;
+
+	checkWellOriented(g);
+        if (validErr != NULL) return;
+}
+
+void
+IsValidOp::checkValid(const PolyhedralSurface *g)
+{
+	unsigned int ngeoms = g->getNumGeometries();
+	vector<const Polygon *>polys(ngeoms);
+
+	for (unsigned int i=0; i<ngeoms; ++i)
+	{
+		const Polygon *p = (const Polygon *)g->getGeometryN(i);
+
+		if (!p->isSimple())
+        	{	
+                validErr = new TopologyValidationError(
+                        TopologyValidationError::eNotOneRing);
+        	}
+		if (validErr != NULL) return;
+
+		checkInvalidCoordinates(p);
+		if (validErr != NULL) return;
+
+		checkClosedRings(p);
+		if (validErr != NULL) return;
+
+		polys[i]=p;
+	}
+
+	GeometryGraph graph(0,g);
+
+	checkConsistentArea3DSurf(&graph);
+	if (validErr!=NULL) return;
+
+	checkWellConnected(g);
+        if (validErr != NULL) return;
+	
+	checkWellOriented(g);
+        if (validErr != NULL) return;
+}
+
+void
 IsValidOp::checkValid(const GeometryCollection *gc)
 {
 	for(unsigned int i=0, ngeoms=gc->getNumGeometries(); i<ngeoms; ++i)
@@ -310,6 +389,21 @@
 	}
 }
 
+void
+IsValidOp::checkConsistentArea3DSurf(GeometryGraph *graph)
+{
+        ConsistentAreaTester cat(graph);
+        bool isValidArea=cat.isNodeConsistentArea3DSurf();
+
+        if (!isValidArea)
+        {
+                validErr=new TopologyValidationError(
+                        TopologyValidationError::eSelfIntersection,
+                        cat.getInvalidPoint());
+                return;
+        }
+}
+
 
 /*private*/
 void
@@ -569,6 +663,70 @@
 }
 
 /*private*/
+/*call this function only with multigeometries(Tin & PolyhedralSurface in fact)*/
+void
+IsValidOp::checkWellConnected(const Geometry *geom)
+{
+
+	for(int i=0; i<geom->getNumGeometries(); i++)
+	{
+		const Polygon *p = (const Polygon *)geom->getGeometryN(i);
+		const int LENGTH = p->getCoordinates()->getSize()-1;
+		int connected = 0;
+		for(int j=0; j<geom->getNumGeometries(); j++)
+		{
+			if(i!=j)
+			{
+			const Polygon *ptmp = (const Polygon *)geom->getGeometryN(j);
+	
+			if (checkAdjacent(p, ptmp)) connected++;
+			}
+		}
+
+		if(!connected)
+		/*The triangle hasn't any neighbour*/
+		{
+		validErr = new TopologyValidationError(
+	                         TopologyValidationError::eSingleFace,
+				p->getCoordinates()->getAt(0));
+	        return;
+		}
+	}
+	return;	
+}
+
+/*call this function only with multigeometries(Tin & PolyhedralSurface in fact)*/	
+void
+IsValidOp::checkWellOriented(const Geometry *g)
+{
+	int *faces = (int*) malloc(g->getNumGeometries() * sizeof(int));
+	if(faces==NULL){
+		return;
+	}
+	for(int i=0; i<g->getNumGeometries(); i++){
+                *(faces + i) = 0;
+        }
+	for(int i=0; i<g->getNumGeometries(); i++){
+	if(!(*(faces + i))){	
+
+	const Polygon *p = (const Polygon *)g->getGeometryN(i);
+		
+		for (int j=0; j<g->getNumGeometries(); j++)
+		{
+	
+		if(i!=j){
+			const Polygon *ptmp = (const Polygon *)g->getGeometryN(j);
+
+			checkNeighbourSameOrientation(p, ptmp, faces, j);
+			if (validErr!=NULL) return;
+		}
+		}
+	}
+	}
+	free(faces);
+}
+
+/*private*/
 void
 IsValidOp::checkInvalidCoordinates(const Polygon *poly)
 {
@@ -614,6 +772,59 @@
 	}
 }
 
+bool
+IsValidOp::checkAdjacent(const Polygon *p, const Polygon *ptmp)
+{
+	for(int i=0; i< p->getCoordinates()->getSize()-1; i++)
+	{	
+	Coordinate pt0 = p->getCoordinates()->getAt(i);
+        Coordinate pt1 = p->getCoordinates()->getAt(i+1);
+
+		for(int j=0; j<ptmp->getCoordinates()->getSize()-1; j++)
+		{
+		Coordinate ptmp0 = ptmp->getCoordinates()->getAt(j);
+	        Coordinate ptmp1 = ptmp->getCoordinates()->getAt(j+1);
+	
+		/*checking the edge*/
+		if(pt0.equals3D(ptmp0) && pt1.equals3D(ptmp1) ||
+		pt0.equals3D(ptmp1) && pt1.equals3D(ptmp0)){
+                return true;
+       	        } 
+
+   		}
+	}
+	return false;
+}	
+
+void
+IsValidOp::checkNeighbourSameOrientation(const Polygon *p, const Polygon *ptmp, int *faces, int index)
+{
+	for(int i=0; i< p->getCoordinates()->getSize()-1; i++)
+        {
+        Coordinate pt0 = p->getCoordinates()->getAt(i);
+        Coordinate pt1 = p->getCoordinates()->getAt(i+1);
+
+		for(int j=0; j<ptmp->getCoordinates()->getSize()-1; j++)
+                {
+	                Coordinate ptmp0 = ptmp->getCoordinates()->getAt(j);
+	       	        Coordinate ptmp1 = ptmp->getCoordinates()->getAt(j+1);
+
+	                if(pt0.equals3D(ptmp0) && pt1.equals3D(ptmp1)){
+				validErr = new TopologyValidationError(
+	                        TopologyValidationError::eNotWellOriented,
+					p->getCoordinates()->getAt(i));
+			return;
+			}
+		
+	                if(pt0.equals3D(ptmp1) && pt1.equals3D(ptmp0)){
+			(*(faces + index))=true;
+			return;
+			}
+		}
+	}
+}
+	
+
 } // namespace geos.operation.valid
 } // namespace geos.operation
 } // namespace geos
--- geos-svn/source/io/WKBReader.cpp	2007-09-24 16:30:20.000000000 +0200
+++ geos-patch/trunk/source/io/WKBReader.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -26,6 +26,8 @@
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/CoordinateSequenceFactory.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/PrecisionModel.h>
@@ -286,6 +288,12 @@
 		case WKBConstants::wkbMultiPolygon :
 			result = readMultiPolygon();
 			break;
+		case WKBConstants::wkbTin :
+			result = readTin();
+                        break;
+		case WKBConstants::wkbPolyhedralSurface :
+			result = readPolyhedralSurface();
+                        break;
 		case WKBConstants::wkbGeometryCollection :
 			result = readGeometryCollection();
 			break;
@@ -440,6 +448,60 @@
 	return factory.createMultiPolygon(geoms);
 }
 
+Tin *
+WKBReader::readTin()
+{
+        int numGeoms = dis.readInt();
+        vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);
+
+        try {
+                for (int i=0; i<numGeoms; i++)
+                {
+                        Geometry *g = readGeometry();
+                        if (!dynamic_cast<Polygon *>(g))
+                        {
+                                stringstream err;
+                                err << BAD_GEOM_TYPE_MSG << " Polygon";
+                                throw  ParseException(err.str());
+                        }
+                        (*geoms)[i] = g;
+                }
+        } catch (...) {
+                for (unsigned int i=0; i<geoms->size(); i++)
+                        delete (*geoms)[i];
+                delete geoms;
+                throw;
+        }
+        return factory.createTin(geoms);
+}
+
+PolyhedralSurface *
+WKBReader::readPolyhedralSurface()
+{
+        int numGeoms = dis.readInt();
+        vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);
+
+        try {
+                for (int i=0; i<numGeoms; i++)
+                {
+                        Geometry *g = readGeometry();
+                        if (!dynamic_cast<Polygon *>(g))
+                        {
+                                stringstream err;
+                                err << BAD_GEOM_TYPE_MSG << " Polygon";
+                                throw  ParseException(err.str());
+                        }
+                        (*geoms)[i] = g;
+                }
+        } catch (...) {
+                for (unsigned int i=0; i<geoms->size(); i++)
+                        delete (*geoms)[i];
+                delete geoms;
+                throw;
+        }
+        return factory.createPolyhedralSurface(geoms);
+}
+
 GeometryCollection *
 WKBReader::readGeometryCollection()
 {
--- geos-svn/source/io/WKBWriter.cpp	2007-09-24 16:30:20.000000000 +0200
+++ geos-patch/trunk/source/io/WKBWriter.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -26,6 +26,8 @@
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/PrecisionModel.h>
 
@@ -86,6 +88,14 @@
 			return writeGeometryCollection(
 				(GeometryCollection &)g,
 				WKBConstants::wkbMultiPolygon);
+		case GEOS_TIN:
+                        return writeGeometryCollection(
+                                (GeometryCollection &)g,
+                                WKBConstants::wkbTin);
+		case GEOS_POLYHEDRALSURFACE:
+                        return writeGeometryCollection(
+                                (GeometryCollection &)g,
+                                WKBConstants::wkbPolyhedralSurface);
 		case GEOS_GEOMETRYCOLLECTION:
 			return writeGeometryCollection(
 				(GeometryCollection &)g,
--- geos-svn/source/io/WKTReader.cpp	2007-09-24 16:30:20.000000000 +0200
+++ geos-patch/trunk/source/io/WKTReader.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -26,6 +26,8 @@
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/CoordinateSequenceFactory.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/PrecisionModel.h>
@@ -206,6 +208,10 @@
 		return readMultiLineStringText(tokenizer);
 	} else if (type=="MULTIPOLYGON") {
 		return readMultiPolygonText(tokenizer);
+	} else if (type=="TIN") {
+                return readTinText(tokenizer);
+	} else if (type=="POLYHEDRALSURFACE") {
+                return readPolyhedralSurfaceText(tokenizer);
 	} else if (type=="GEOMETRYCOLLECTION") {
 		return readGeometryCollectionText(tokenizer);
 	}
@@ -400,6 +406,42 @@
 	return ret;
 }
 
+Tin* WKTReader::readTinText(StringTokenizer *tokenizer) {
+        string nextToken=getNextEmptyOrOpener(tokenizer);
+        if (nextToken=="EMPTY") {
+                return geometryFactory->createTin(NULL);
+        }
+        vector<Geometry *> *polygons=new vector<Geometry *>();
+        Polygon *polygon=readPolygonText(tokenizer);
+        polygons->push_back(polygon);
+        nextToken=getNextCloserOrComma(tokenizer);
+        while(nextToken==",") {
+                Polygon *polygon=readPolygonText(tokenizer);
+                polygons->push_back(polygon);
+                nextToken=getNextCloserOrComma(tokenizer);
+        }
+        Tin *ret = geometryFactory->createTin(polygons);
+        return ret;
+}
+
+PolyhedralSurface* WKTReader::readPolyhedralSurfaceText(StringTokenizer *tokenizer) {
+        string nextToken=getNextEmptyOrOpener(tokenizer);
+        if (nextToken=="EMPTY") {
+                return geometryFactory->createPolyhedralSurface(NULL);
+        }
+        vector<Geometry *> *polygons=new vector<Geometry *>();
+        Polygon *polygon=readPolygonText(tokenizer);
+        polygons->push_back(polygon);
+        nextToken=getNextCloserOrComma(tokenizer);
+        while(nextToken==",") {
+                Polygon *polygon=readPolygonText(tokenizer);
+                polygons->push_back(polygon);
+                nextToken=getNextCloserOrComma(tokenizer);
+        }
+        PolyhedralSurface *ret = geometryFactory->createPolyhedralSurface(polygons);
+        return ret;
+}
+
 GeometryCollection* WKTReader::readGeometryCollectionText(StringTokenizer *tokenizer) {
 	string nextToken=getNextEmptyOrOpener(tokenizer);
 	if (nextToken=="EMPTY") {
--- geos-svn/source/io/WKTWriter.cpp	2007-09-24 16:30:20.000000000 +0200
+++ geos-patch/trunk/source/io/WKTWriter.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -24,6 +24,8 @@
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/PrecisionModel.h>
 
@@ -179,6 +181,10 @@
 		appendMultiLineStringTaggedText((MultiLineString*)geometry, level, writer);
 	} else if (typeid(*geometry)==typeid(MultiPolygon)) {
 		appendMultiPolygonTaggedText((MultiPolygon*)geometry, level, writer);
+	} else if (typeid(*geometry)==typeid(Tin)) {
+                appendTinTaggedText((Tin*)geometry, level, writer);
+	} else if (typeid(*geometry)==typeid(PolyhedralSurface)) {
+                appendPolyhedralSurfaceTaggedText((PolyhedralSurface*)geometry, level, writer);
 	} else if (typeid(*geometry)==typeid(GeometryCollection)) {
 		appendGeometryCollectionTaggedText((GeometryCollection*)geometry, level, writer);
 	} else {
@@ -235,6 +241,16 @@
 	appendMultiPolygonText(multiPolygon, level, writer);
 }
 
+void WKTWriter::appendTinTaggedText(const Tin *tin, int level, Writer *writer) {
+        writer->write("TIN ");
+        appendTinText(tin, level, writer);
+}
+
+void WKTWriter::appendPolyhedralSurfaceTaggedText(const PolyhedralSurface *polyhedralSurface, int level, Writer *writer) {
+        writer->write("POLYHEDRALSURFACE ");
+        appendPolyhedralSurfaceText(polyhedralSurface, level, writer);
+}
+
 void WKTWriter::appendGeometryCollectionTaggedText(const GeometryCollection *geometryCollection, int level,Writer *writer) {
 	writer->write("GEOMETRYCOLLECTION ");
 	appendGeometryCollectionText(geometryCollection, level, writer);
@@ -384,6 +400,48 @@
 	}
 }
 
+void WKTWriter::appendTinText(const Tin *tin, int level, Writer *writer) {
+        if (tin->isEmpty()) {
+                writer->write("EMPTY");
+        } else {
+                int level2=level;
+                bool doIndent=false;
+                writer->write("(");
+                for (unsigned int i=0, n=tin->getNumGeometries();
+                                i < n; i++)
+                {
+                        if (i>0) {
+                                writer->write(", ");
+                                level2=level+1;
+                                doIndent=true;
+                        }
+                        appendPolygonText((Polygon *) tin->getGeometryN(i), level2, doIndent, writer);
+                }
+                writer->write(")");
+        }
+}
+
+void WKTWriter::appendPolyhedralSurfaceText(const PolyhedralSurface *polyhedralSurface, int level, Writer *writer) {
+        if (polyhedralSurface->isEmpty()) {
+                writer->write("EMPTY");
+        } else {
+                int level2=level;
+                bool doIndent=false;
+                writer->write("(");
+                for (unsigned int i=0, n=polyhedralSurface->getNumGeometries();
+                                i < n; i++)
+                {
+                        if (i>0) {
+                                writer->write(", ");
+                                level2=level+1;
+                                doIndent=true;
+                        }
+                        appendPolygonText((Polygon *) polyhedralSurface->getGeometryN(i), level2, doIndent, writer);
+                }
+                writer->write(")");
+        }
+}
+
 void
 WKTWriter::appendGeometryCollectionText(
 		const GeometryCollection *geometryCollection,
--- geos-svn/source/headers/geos/geom/PolyhedralSurface.h	1970-01-01 01:00:00.000000000 +0100
+++ geos-patch/trunk/source/headers/geos/geom/PolyhedralSurface.h	2007-09-24 16:50:49.000000000 +0200
@@ -0,0 +1,112 @@
+/**********************************************************************
+ * $Id:$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2001-2002 Vivid Solutions Inc.
+ * Copyright (C) 2005 2006 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#ifndef GEOS_GEOS_POLYHEDRALSURFACE_H
+#define GEOS_GEOS_POLYHEDRALSURFACE_H
+
+#include <string>
+#include <vector>
+#include <geos/platform.h>
+#include <geos/geom/GeometryCollection.h>
+#include <geos/geom/Dimension.h> // for Dimension::DimensionType
+
+#include <geos/inline.h>
+
+// Forward declarations
+namespace geos {
+	namespace geom { // geos::geom
+		class Coordinate;
+		class CoordinateArraySequence;
+		class MultiPoint;
+	}
+}
+
+
+namespace geos {
+namespace geom { // geos::geom
+
+/// Basic implementation of <code>MultiPolygon</code>.
+class PolyhedralSurface: public GeometryCollection {
+
+public:
+
+	friend class GeometryFactory;
+
+	virtual ~PolyhedralSurface();
+
+	/// Returns surface dimension (2)
+	Dimension::DimensionType getDimension() const;
+
+	/// Returns 1 (POLYHEDRALSURFACE boundary is MultiLineString)
+	int getBoundaryDimension() const;
+
+	/**
+	 * \brief
+	 * Returns a MultiLineString composed of one LineString for
+	 * each of the composing Polygon's shells 
+	 */
+	Geometry* getBoundary() const;
+
+	std::string getGeometryType() const;
+
+	virtual GeometryTypeId getGeometryTypeId() const;
+
+	bool isSimple() const;
+
+	bool equalsExact(const Geometry *other, double tolerance=0) const;
+
+	Geometry *clone() const;
+
+protected:
+
+	/**
+	 * \brief Construct a POLYHEDRALSURFACE
+	 *
+	 * @param newPolys
+	 *	the <code>Polygon</code>s for this <code>POLYHEDRALSURFACE</code>,
+	 *	or <code>null</code> or an empty array to create the empty
+	 *	geometry. Elements may be empty <code>Polygon</code>s, but
+	 *	not <code>null</code>s.
+	 *	The polygons must conform to the assertions specified in the
+	 *	<A HREF="http://www.opengis.org/techno/specs.htm">
+	 *	OpenGIS Simple Features Specification for SQL
+	 *	</A>.
+	 *
+	 *	Constructed object will take ownership of
+	 *	the vector and its elements.
+	 *
+	 * @param newFactory
+	 * 	The GeometryFactory used to create this geometry
+	 *	Caller must keep the factory alive for the life-time
+	 *	of the constructed POLYHEDRALSURFACE.
+	 */
+	PolyhedralSurface(std::vector<Geometry *> *newPolys, const GeometryFactory *newFactory);
+
+	PolyhedralSurface(const PolyhedralSurface &mp);
+
+
+};
+
+
+} // namespace geos::geom
+} // namespace geos
+
+#ifdef GEOS_INLINE
+# include "geos/geom/PolyhedralSurface.inl"
+#endif
+
+#endif // ndef GEOS_GEOS_POLYHEDRALSURFACE_H
+
--- geos-svn/source/headers/geos/geom/PolyhedralSurface.inl	1970-01-01 01:00:00.000000000 +0100
+++ geos-patch/trunk/source/headers/geos/geom/PolyhedralSurface.inl	2007-09-24 16:50:49.000000000 +0200
@@ -0,0 +1,42 @@
+/***********************************************************************
+ ** $id:$
+ **
+ ** GEOS - Geometry Engine Open Source
+ ** http://geos.refractions.net
+ **
+ **Copyright (C) 2005-2006 Refractions Research Inc.
+ **
+ **This is free software; you can redistribute and/or modify it under
+ ** the terms of the GNU Lesser General Public Licence as published
+ ** by the Free Software Foundation.
+ ** See the COPYING file for more information.
+ **
+ ***********************************************************************/
+
+#ifndef GEOS_GEOM_POLYHEDRALSURFACE_INL
+#define GEOS_GEOM_POLYHEDRALSURFACE_INL
+
+#include <geos/geom/PolyhedralSurface.h>
+#include <geos/geom/GeometryCollection.h>
+
+namespace geos {
+namespace geom { // geos::geom
+
+INLINE
+PolyhedralSurface::PolyhedralSurface(const PolyhedralSurface &mp)
+        :
+        GeometryCollection(mp)
+{
+}
+
+INLINE Geometry*
+PolyhedralSurface::clone() const
+{
+        return new PolyhedralSurface(*this);
+}
+
+} // namespace geos::geom
+} // namespace geos
+
+#endif // GEOS_GEOM_POLYHEDRALSURFACE_INL
+
--- geos-svn/source/headers/geos/geom/Tin.inl	1970-01-01 01:00:00.000000000 +0100
+++ geos-patch/trunk/source/headers/geos/geom/Tin.inl	2007-09-24 16:50:49.000000000 +0200
@@ -0,0 +1,42 @@
+/***********************************************************************
+ ** $id:$
+ **
+ ** GEOS - Geometry Engine Open Source
+ ** http://geos.refractions.net
+ **
+ **Copyright (C) 2005-2006 Refractions Research Inc.
+ **
+ **This is free software; you can redistribute and/or modify it under
+ ** the terms of the GNU Lesser General Public Licence as published
+ ** by the Free Software Foundation.
+ ** See the COPYING file for more information.
+ **
+ ***********************************************************************/
+
+#ifndef GEOS_GEOM_TIN_INL
+#define GEOS_GEOM_TIN_INL
+
+#include <geos/geom/Tin.h>
+#include <geos/geom/GeometryCollection.h>
+
+namespace geos {
+namespace geom { // geos::geom
+
+INLINE
+Tin::Tin(const Tin &mp)
+        :
+        GeometryCollection(mp)
+{
+}
+
+INLINE Geometry*
+Tin::clone() const
+{
+        return new Tin(*this);
+}
+
+} // namespace geos::geom
+} // namespace geos
+
+#endif // GEOS_GEOM_TIN_INL
+
--- geos-svn/source/headers/geos/geom/Tin.h	1970-01-01 01:00:00.000000000 +0100
+++ geos-patch/trunk/source/headers/geos/geom/Tin.h	2007-09-24 16:50:49.000000000 +0200
@@ -0,0 +1,112 @@
+/**********************************************************************
+ * $Id:$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2001-2002 Vivid Solutions Inc.
+ * Copyright (C) 2005 2006 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#ifndef GEOS_GEOS_TIN_H
+#define GEOS_GEOS_TIN_H
+
+#include <string>
+#include <vector>
+#include <geos/platform.h>
+#include <geos/geom/GeometryCollection.h>
+#include <geos/geom/Dimension.h> // for Dimension::DimensionType
+
+#include <geos/inline.h>
+
+// Forward declarations
+namespace geos {
+	namespace geom { // geos::geom
+		class Coordinate;
+		class CoordinateArraySequence;
+		class MultiPoint;
+	}
+}
+
+
+namespace geos {
+namespace geom { // geos::geom
+
+/// Basic implementation of <code>MultiPolygon</code>.
+class Tin: public GeometryCollection {
+
+public:
+
+	friend class GeometryFactory;
+
+	virtual ~Tin();
+
+	/// Returns surface dimension (2)
+	Dimension::DimensionType getDimension() const;
+
+	/// Returns 1 (Tin boundary is MultiLineString)
+	int getBoundaryDimension() const;
+
+	/**
+	 * \brief
+	 * Returns a MultiLineString composed of one LineString for
+	 * each of the composing Polygon's shells 
+	 */
+	Geometry* getBoundary() const;
+
+	std::string getGeometryType() const;
+
+	virtual GeometryTypeId getGeometryTypeId() const;
+
+	bool isSimple() const;
+
+	bool equalsExact(const Geometry *other, double tolerance=0) const;
+
+	Geometry *clone() const;
+
+protected:
+
+	/**
+	 * \brief Construct a Tin
+	 *
+	 * @param newPolys
+	 *	the <code>Polygon</code>s for this <code>Tin</code>,
+	 *	or <code>null</code> or an empty array to create the empty
+	 *	geometry. Elements may be empty <code>Polygon</code>s, but
+	 *	not <code>null</code>s.
+	 *	The polygons must conform to the assertions specified in the
+	 *	<A HREF="http://www.opengis.org/techno/specs.htm">
+	 *	OpenGIS Simple Features Specification for SQL
+	 *	</A>.
+	 *
+	 *	Constructed object will take ownership of
+	 *	the vector and its elements.
+	 *
+	 * @param newFactory
+	 * 	The GeometryFactory used to create this geometry
+	 *	Caller must keep the factory alive for the life-time
+	 *	of the constructed Tin.
+	 */
+	Tin(std::vector<Geometry *> *newPolys, const GeometryFactory *newFactory);
+
+	Tin(const Tin &mp);
+
+
+};
+
+
+} // namespace geos::geom
+} // namespace geos
+
+#ifdef GEOS_INLINE
+# include "geos/geom/Tin.inl"
+#endif
+
+#endif // ndef GEOS_GEOS_TIN_H
+
--- geos-svn/source/headers/geos/geom/Makefile.am	2007-09-24 16:30:22.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/geom/Makefile.am	2007-09-24 16:50:49.000000000 +0200
@@ -43,6 +43,10 @@
 	MultiPoint.h				\
 	MultiPolygon.h				\
 	MultiPolygon.inl			\
+	Tin.h					\
+	Tin.inl					\
+	PolyhedralSurface.h			\
+	PolyhedralSurface.inl			\
 	Point.h					\
 	Polygon.h				\
 	PrecisionModel.h			\
--- geos-svn/source/headers/geos/geom/GeometryFactory.h	2007-09-24 16:30:22.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/geom/GeometryFactory.h	2007-09-24 16:50:49.000000000 +0200
@@ -40,6 +40,8 @@
 		class MultiLineString;
 		class MultiPoint;
 		class MultiPolygon;
+		class Tin;
+		class PolyhedralSurface;
 		class Point;
 		class Polygon;
 		class PrecisionModel;
@@ -194,6 +196,26 @@
 	MultiPolygon* createMultiPolygon(
 			const std::vector<Geometry *> &fromPolys) const;
 
+	/// Construct an EMPTY Tin 
+	Tin* createTin() const;
+
+	/// Construct a Tin taking ownership of given arguments
+	Tin* createTin(std::vector<Geometry *> *newPolys) const;
+
+	/// Construct a Tin with a deep-copy of given arguments
+	Tin* createTin(
+			const std::vector<Geometry *> &fromPolys) const;
+
+	/// Construct an EMPTY PolyhedralSurface 
+	PolyhedralSurface* createPolyhedralSurface() const;
+
+	/// Construct a PolyhedralSurface taking ownership of given arguments
+	PolyhedralSurface* createPolyhedralSurface(std::vector<Geometry *> *newPolys) const;
+
+	/// Construct a PolyhedralSurface with a deep-copy of given arguments
+	PolyhedralSurface* createPolyhedralSurface(
+			const std::vector<Geometry *> &fromPolys) const;
+
 	/// Construct an EMPTY LinearRing 
 	LinearRing* createLinearRing() const;
 
--- geos-svn/source/headers/geos/operation/valid/IsValidOp.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/operation/valid/IsValidOp.h	2007-09-24 17:44:44.000000000 +0200
@@ -34,6 +34,8 @@
 		class Polygon;
 		class GeometryCollection;
 		class MultiPolygon;
+		class Tin;
+		class PolyhedralSurface;
 		class MultiLineString;
 	}
 	namespace geomgraph {
@@ -65,8 +67,11 @@
 	void checkValid(const geom::LineString *g);
 	void checkValid(const geom::Polygon *g);
 	void checkValid(const geom::MultiPolygon *g);
+	void checkValid(const geom::Tin *g);
+	void checkValid(const geom::PolyhedralSurface *g);
 	void checkValid(const geom::GeometryCollection *gc);
 	void checkConsistentArea(geomgraph::GeometryGraph *graph);
+	void checkConsistentArea3DSurf(geomgraph::GeometryGraph *graph);
 
 
 	/**
@@ -170,6 +175,12 @@
 
 	void checkInvalidCoordinates(const geom::CoordinateSequence *cs);
 
+	void checkWellConnected(const geom::Geometry *geom);
+
+	void checkWellOriented(const geom::Geometry *geom);
+
+	void checkPositiveOrientation(const geom::Polygon *poly);
+
 	void checkInvalidCoordinates(const geom::Polygon *poly);
 
 	void checkClosedRings(const geom::Polygon *poly);
@@ -247,6 +258,9 @@
 		isSelfTouchingRingFormingHoleValid = isValid;
 	}
 
+	bool checkAdjacent(const geom::Polygon *p, const geom::Polygon *ptmp);
+
+	void checkNeighbourSameOrientation(const geom::Polygon *p, const geom::Polygon *ptmp, int *faces, int index);
 };
 
 } // namespace geos.operation.valid
--- geos-svn/source/headers/geos/io/WKBReader.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/io/WKBReader.h	2007-09-24 16:50:49.000000000 +0200
@@ -38,6 +38,8 @@
 		class MultiPoint;
 		class MultiLineString;
 		class MultiPolygon;
+		class Tin;
+		class PolyhedralSurface;
 		class PrecisionModel;
 
 	} // namespace geom
@@ -138,6 +140,12 @@
 
 	geom::MultiPolygon *readMultiPolygon();
 		// throws IOException, ParseException
+		
+	geom::Tin *readTin();
+                // throws IOException, ParseException
+
+	geom::PolyhedralSurface *readPolyhedralSurface();
+                // throws IOException, ParseException
 
 	geom::GeometryCollection *readGeometryCollection();
 		// throws IOException, ParseException
--- geos-svn/source/headers/geos/io/WKBWriter.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/io/WKBWriter.h	2007-09-24 16:50:49.000000000 +0200
@@ -34,6 +34,8 @@
 		class MultiPoint;
 		class MultiLineString;
 		class MultiPolygon;
+		class Tin;
+		class PolyhedralSurface;
 		class PrecisionModel;
 
 	} // namespace geom
--- geos-svn/source/headers/geos/io/WKTReader.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/io/WKTReader.h	2007-09-24 16:50:49.000000000 +0200
@@ -37,6 +37,8 @@
 		class Polygon;
 		class MultiPoint;
 		class MultiLineString;
+		class Tin;
+		class PolyhedralSurface;
 		class MultiPolygon;
 		class PrecisionModel;
 	} 
@@ -92,6 +94,8 @@
 	geom::Polygon* readPolygonText(io::StringTokenizer *tokenizer);
 	geom::MultiLineString* readMultiLineStringText(io::StringTokenizer *tokenizer);
 	geom::MultiPolygon* readMultiPolygonText(io::StringTokenizer *tokenizer);
+	geom::Tin* readTinText(io::StringTokenizer *tokenizer);
+	geom::PolyhedralSurface* readPolyhedralSurfaceText(io::StringTokenizer *tokenizer);
 	geom::GeometryCollection* readGeometryCollectionText(io::StringTokenizer *tokenizer);
 private:
 	const geom::GeometryFactory *geometryFactory;
--- geos-svn/source/headers/geos/io/WKTWriter.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/io/WKTWriter.h	2007-09-24 16:50:49.000000000 +0200
@@ -33,6 +33,8 @@
 		class MultiPoint;
 		class MultiLineString;
 		class MultiPolygon;
+		class Tin;
+		class PolyhedralSurface;
 		class PrecisionModel;
 	} 
 	namespace io {
@@ -146,6 +148,14 @@
 			const geom::MultiPolygon *multiPolygon,
 			int level, Writer *writer);
 
+	void appendTinTaggedText(
+                        const geom::Tin *tin,
+                        int level, Writer *writer);
+
+	void appendPolyhedralSurfaceTaggedText(
+                        const geom::PolyhedralSurface *polyhedralSurface,
+                        int level, Writer *writer);
+
 	void appendGeometryCollectionTaggedText(
 			const geom::GeometryCollection *geometryCollection,
 			int level,Writer *writer);
@@ -178,6 +188,14 @@
 			const geom::MultiPolygon *multiPolygon,
 			int level, Writer *writer);
 
+	void appendTinText(
+                        const geom::Tin *tin,
+                        int level, Writer *writer);
+
+	void appendPolyhedralSurfaceText(
+                        const geom::PolyhedralSurface *polyhedralSurface,
+                        int level, Writer *writer);
+
 	void appendGeometryCollectionText(
 			const geom::GeometryCollection *geometryCollection,
 			int level,Writer *writer);
--- geos-svn/source/headers/geos/io/WKBConstants.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/io/WKBConstants.h	2007-09-24 16:50:49.000000000 +0200
@@ -35,7 +35,9 @@
 	const int wkbMultiPoint = 4;
 	const int wkbMultiLineString = 5;
 	const int wkbMultiPolygon = 6;
-	const int wkbGeometryCollection = 7;
+	const int wkbTin = 7;
+        const int wkbPolyhedralSurface = 8;
+	const int wkbGeometryCollection = 9;
 }
 
 } // namespace geos::io
--- geos-svn/source/headers/geos/geom/Geometry.h	2007-09-24 16:30:22.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/geom/Geometry.h	2007-09-24 16:50:49.000000000 +0200
@@ -69,6 +69,10 @@
 	GEOS_MULTILINESTRING,
 	/// a collection of polygons
 	GEOS_MULTIPOLYGON,
+	///A triangular irregular network
+	GEOS_TIN,
+	/// a polyhedralsurface
+	GEOS_POLYHEDRALSURFACE,
 	/// a collection of heterogeneus geometries
 	GEOS_GEOMETRYCOLLECTION
 };
--- geos-svn/source/headers/geos/operation/valid/TopologyValidationError.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/operation/valid/TopologyValidationError.h	2007-09-24 17:37:58.000000000 +0200
@@ -48,7 +48,11 @@
 		eDuplicatedRings,
 		eTooFewPoints,
 		eInvalidCoordinate,
-		eRingNotClosed
+		eRingNotClosed,
+		eNotOneRing,
+		eNotATriangle,
+		eSingleFace,
+		eNotWellOriented
 	};
 
 	TopologyValidationError(int newErrorType, const geom::Coordinate& newPt);
--- geos-svn/source/operation/valid/TopologyValidationError.cpp	2007-09-24 16:30:19.000000000 +0200
+++ geos-patch/trunk/source/operation/valid/TopologyValidationError.cpp	2007-09-24 17:38:02.000000000 +0200
@@ -42,7 +42,11 @@
 	"Duplicate Rings",
 	"Too few points in geometry component",
 	"Invalid Coordinate",
-	"Ring is not closed"
+	"Ring is not closed",
+	"More than one ring",
+	"Not a triangle",
+	"Isolated face",
+	"Two neighbour faces are not in the same orientation"
 };
 
 TopologyValidationError::TopologyValidationError(int newErrorType,
--- geos-svn/source/headers/geos/operation/valid/ConsistentAreaTester.h	2007-09-24 16:30:23.000000000 +0200
+++ geos-patch/trunk/source/headers/geos/operation/valid/ConsistentAreaTester.h	2007-09-24 16:50:49.000000000 +0200
@@ -86,6 +86,7 @@
 	geom::Coordinate& getInvalidPoint();
 
 	bool isNodeConsistentArea();
+	bool isNodeConsistentArea3DSurf();
 
 	/**
 	 * Checks for two duplicate rings in an area.
--- geos-svn/source/operation/valid/ConsistentAreaTester.cpp	2007-09-24 16:30:19.000000000 +0200
+++ geos-patch/trunk/source/operation/valid/ConsistentAreaTester.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -75,6 +75,24 @@
 	return isNodeEdgeAreaLabelsConsistent();
 }
 
+bool
+ConsistentAreaTester::isNodeConsistentArea3DSurf()
+{
+        using geomgraph::index::SegmentIntersector;
+
+        /**
+        * To fully check validity, it is necessary to
+        * compute ALL intersections, including self-intersections within a single edge.
+        */
+        auto_ptr<SegmentIntersector> intersector(geomGraph->computeSelfNodes(&li, true));
+        if (intersector->hasProperIntersection()) {
+                invalidPoint=intersector->getProperIntersectionPoint();
+                return false;
+        }
+	return true;
+}
+
+
 /*private*/
 bool
 ConsistentAreaTester::isNodeEdgeAreaLabelsConsistent()
--- geos-svn/source/geomgraph/GeometryGraph.cpp	2007-09-24 16:30:21.000000000 +0200
+++ geos-patch/trunk/source/geomgraph/GeometryGraph.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -38,6 +38,8 @@
 #include <geos/geom/MultiPoint.h>
 #include <geos/geom/MultiLineString.h>
 #include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/GeometryCollection.h>
 
 #include <geos/inline.h>
@@ -187,6 +189,10 @@
 		addCollection((MultiLineString*) g);
 	else if (typeid(*g)==typeid(MultiPolygon))
 		addCollection((MultiPolygon*) g);
+	else if (typeid(*g)==typeid(Tin))
+                addCollection((Tin*) g);
+	else if (typeid(*g)==typeid(PolyhedralSurface))
+                addCollection((PolyhedralSurface*) g);
 	else if (typeid(*g)==typeid(GeometryCollection))
 		addCollection((GeometryCollection*) g);
 	else {
--- geos-svn/capi/geos_c.cpp	2007-09-24 16:30:27.000000000 +0200
+++ geos-patch/trunk/capi/geos_c.cpp	2007-09-24 16:50:49.000000000 +0200
@@ -22,6 +22,8 @@
 #include <geos/geom/MultiPoint.h> 
 #include <geos/geom/MultiLineString.h> 
 #include <geos/geom/MultiPolygon.h> 
+#include <geos/geom/Tin.h>
+#include <geos/geom/PolyhedralSurface.h>
 #include <geos/geom/LinearRing.h> 
 #include <geos/geom/LineString.h> 
 #include <geos/geom/PrecisionModel.h> 
@@ -1278,6 +1280,12 @@
 			case GEOS_MULTIPOLYGON:
 				g = geomFactory->createMultiPolygon(vgeoms);
 				break;
+			case GEOS_TIN:
+                                g = geomFactory->createTin(vgeoms);
+                                break;
+			case GEOS_POLYHEDRALSURFACE:
+                                g = geomFactory->createPolyhedralSurface(vgeoms);
+                                break;
 			default:
 				ERROR_MESSAGE("Unsupported type request for PostGIS2GEOS_collection");
 				g = NULL;
--- geos-svn/capi/geos_c.h.in	2007-09-24 16:30:27.000000000 +0200
+++ geos-patch/trunk/capi/geos_c.h.in	2007-09-24 16:50:49.000000000 +0200
@@ -108,6 +108,8 @@
 	GEOS_MULTIPOINT,
 	GEOS_MULTILINESTRING,
 	GEOS_MULTIPOLYGON,
+	GEOS_TIN,
+	GEOS_POLYHEDRALSURFACE,
 	GEOS_GEOMETRYCOLLECTION
 };
 


More information about the geos-devel mailing list