www.pudn.com > mitab.rar > ogrgeometrycollection.cpp


/****************************************************************************** 
 * $Id: ogrgeometrycollection.cpp,v 1.29 2005/02/22 12:38:01 fwarmerdam Exp $ 
 * 
 * Project:  OpenGIS Simple Features Reference Implementation 
 * Purpose:  The OGRGeometryCollection class. 
 * Author:   Frank Warmerdam, warmerda@home.com 
 * 
 ****************************************************************************** 
 * Copyright (c) 1999, Frank Warmerdam 
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE. 
 ****************************************************************************** 
 * 
 * $Log: ogrgeometrycollection.cpp,v $ 
 * Revision 1.29  2005/02/22 12:38:01  fwarmerdam 
 * rename Equal/Intersect to Equals/Intersects 
 * 
 * Revision 1.28  2004/07/10 04:51:22  warmerda 
 * added closeRings 
 * 
 * Revision 1.27  2004/02/22 09:52:04  dron 
 * Fix compirison casting problems in OGRGeometryCollection::Equal(). 
 * 
 * Revision 1.26  2004/02/21 15:36:14  warmerda 
 * const correctness updates for geometry: bug 289 
 * 
 * Revision 1.25  2004/01/16 21:57:16  warmerda 
 * fixed up EMPTY support 
 * 
 * Revision 1.24  2004/01/16 21:20:00  warmerda 
 * Added EMPTY support 
 * 
 * Revision 1.23  2003/08/27 15:40:37  warmerda 
 * added support for generating DB2 V7.2 compatible WKB 
 * 
 * Revision 1.22  2003/06/09 13:48:54  warmerda 
 * added DB2 V7.2 byte order hack 
 * 
 * Revision 1.21  2003/05/28 19:16:42  warmerda 
 * fixed up argument names and stuff for docs 
 * 
 * Revision 1.20  2003/03/31 15:55:42  danmo 
 * Added C API function docs 
 * 
 * Revision 1.19  2003/03/07 21:32:52  warmerda 
 * fixed bug with coordinate dimension reading from WKB 
 * 
 * Revision 1.18  2003/01/07 16:44:27  warmerda 
 * added removeGeometry 
 * 
 * Revision 1.17  2002/09/11 13:47:17  warmerda 
 * preliminary set of fixes for 3D WKB enum 
 * 
 * Revision 1.16  2002/05/02 19:45:36  warmerda 
 * added flattenTo2D() method 
 * 
 * Revision 1.15  2002/03/05 14:25:14  warmerda 
 * expand tabs 
 * 
 * Revision 1.14  2001/11/01 17:20:33  warmerda 
 * added DISABLE_OGRGEOM_TRANSFORM macro 
 * 
 * Revision 1.13  2001/09/21 16:24:20  warmerda 
 * added transform() and transformTo() methods 
 * 
 * Revision 1.12  2001/08/30 02:06:19  warmerda 
 * fixed array overrun error in exportToWkt() 
 * 
 * Revision 1.11  2001/07/18 05:03:05  warmerda 
 * added CPL_CVSID 
 * 
 * Revision 1.10  2001/05/24 18:06:30  warmerda 
 * use addGeometryDirectly when parsing WKT 
 * 
 * Revision 1.9  1999/11/18 19:02:19  warmerda 
 * expanded tabs 
 * 
 * Revision 1.8  1999/11/04 16:26:29  warmerda 
 * Implemented addGeometryDirectly(). 
 * 
 * Revision 1.7  1999/09/01 11:50:40  warmerda 
 * Fixed CPLAssert on legal geometry types. 
 * 
 * Revision 1.6  1999/07/27 00:48:11  warmerda 
 * Added Equal() support 
 * 
 * Revision 1.5  1999/07/06 21:36:47  warmerda 
 * tenatively added getEnvelope() and Intersect() 
 * 
 * Revision 1.4  1999/06/25 20:44:43  warmerda 
 * implemented assignSpatialReference, carry properly 
 * 
 * Revision 1.3  1999/05/31 20:43:04  warmerda 
 * added empty method, implement createFromWkt(), added mline/mpoint 
 * 
 * Revision 1.2  1999/05/31 14:59:06  warmerda 
 * added documentation 
 * 
 * Revision 1.1  1999/05/23 05:34:36  warmerda 
 * New 
 * 
 */ 
 
#include "ogr_geometry.h" 
#include "ogr_p.h" 
 
CPL_CVSID("$Id: ogrgeometrycollection.cpp,v 1.29 2005/02/22 12:38:01 fwarmerdam Exp $"); 
 
/************************************************************************/ 
/*                       OGRGeometryCollection()                        */ 
/************************************************************************/ 
 
/** 
 * Create an empty geometry collection. 
 */ 
 
OGRGeometryCollection::OGRGeometryCollection() 
 
{ 
    nGeomCount = 0; 
    papoGeoms = NULL; 
    nCoordinateDimension = 2; 
} 
 
/************************************************************************/ 
/*                       ~OGRGeometryCollection()                       */ 
/************************************************************************/ 
 
OGRGeometryCollection::~OGRGeometryCollection() 
 
{ 
    empty(); 
    nCoordinateDimension = 2; 
} 
 
/************************************************************************/ 
/*                               empty()                                */ 
/************************************************************************/ 
 
void OGRGeometryCollection::empty() 
 
{ 
    if( papoGeoms != NULL ) 
    { 
        for( int i = 0; i < nGeomCount; i++ ) 
        { 
            delete papoGeoms[i]; 
        } 
        OGRFree( papoGeoms ); 
    } 
 
    nGeomCount = 0; 
    papoGeoms = NULL; 
} 
 
 
/************************************************************************/ 
/*                               clone()                                */ 
/************************************************************************/ 
 
OGRGeometry *OGRGeometryCollection::clone() const 
 
{ 
    OGRGeometryCollection       *poNewGC; 
 
    poNewGC = new OGRGeometryCollection; 
    poNewGC->assignSpatialReference( getSpatialReference() ); 
 
    for( int i = 0; i < nGeomCount; i++ ) 
    { 
        poNewGC->addGeometry( papoGeoms[i] ); 
    } 
 
    return poNewGC; 
} 
 
/************************************************************************/ 
/*                          getGeometryType()                           */ 
/************************************************************************/ 
 
OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const 
 
{ 
    if( getCoordinateDimension() == 3 ) 
        return wkbGeometryCollection25D; 
    else 
        return wkbGeometryCollection; 
} 
 
/************************************************************************/ 
/*                            getDimension()                            */ 
/************************************************************************/ 
 
int OGRGeometryCollection::getDimension() const 
 
{ 
    return 2; // This isn't strictly correct.  It should be based on members. 
} 
 
/************************************************************************/ 
/*                       getCoordinateDimension()                       */ 
/*                                                                      */ 
/*      Returning 2 is a dubious solution.  This should at least be     */ 
/*      overridden by some of the subclasses.                           */ 
/************************************************************************/ 
 
int OGRGeometryCollection::getCoordinateDimension() const 
 
{ 
    if( nCoordinateDimension == 0 ) 
    { 
        ((OGRGeometryCollection *)this)->nCoordinateDimension = 2; 
 
        for( int i = 0; i < nGeomCount; i++ ) 
            if( papoGeoms[i]->getCoordinateDimension() == 3 ) 
                ((OGRGeometryCollection *)this)->nCoordinateDimension = 3; 
    } 
 
    return nCoordinateDimension; 
} 
 
/************************************************************************/ 
/*                            flattenTo2D()                             */ 
/************************************************************************/ 
 
void OGRGeometryCollection::flattenTo2D() 
 
{ 
    for( int i = 0; i < nGeomCount; i++ ) 
        papoGeoms[i]->flattenTo2D(); 
 
    nCoordinateDimension = 2; 
} 
 
/************************************************************************/ 
/*                          getGeometryName()                           */ 
/************************************************************************/ 
 
const char * OGRGeometryCollection::getGeometryName() const 
 
{ 
    return "GEOMETRYCOLLECTION"; 
} 
 
/************************************************************************/ 
/*                          getNumGeometries()                          */ 
/************************************************************************/ 
 
/** 
 * Fetch number of geometries in container. 
 * 
 * This method relates to the SFCOM IGeometryCollect::get_NumGeometries() 
 * method. 
 * 
 * @return count of children geometries.  May be zero. 
 */ 
 
int OGRGeometryCollection::getNumGeometries() const 
 
{ 
    return nGeomCount; 
} 
 
/************************************************************************/ 
/*                           getGeometryRef()                           */ 
/************************************************************************/ 
 
/** 
 * Fetch geometry from container. 
 * 
 * This method returns a pointer to an geometry within the container.  The 
 * returned geometry remains owned by the container, and should not be 
 * modified.  The pointer is only valid untill the next change to the 
 * geometry container.  Use IGeometry::clone() to make a copy. 
 * 
 * This method relates to the SFCOM IGeometryCollection::get_Geometry() method. 
 * 
 * @param i the index of the geometry to fetch, between 0 and 
 *          getNumGeometries() - 1. 
 * @return pointer to requested geometry. 
 */ 
 
OGRGeometry * OGRGeometryCollection::getGeometryRef( int i )  
 
{ 
    if( i < 0 || i >= nGeomCount ) 
        return NULL; 
    else 
        return papoGeoms[i]; 
} 
 
const OGRGeometry * OGRGeometryCollection::getGeometryRef( int i ) const 
 
{ 
    if( i < 0 || i >= nGeomCount ) 
        return NULL; 
    else 
        return papoGeoms[i]; 
} 
 
/************************************************************************/ 
/*                            addGeometry()                             */ 
/*                                                                      */ 
/*      Add a new geometry to a collection.  Subclasses should          */ 
/*      override this to verify the type of the new geometry, and       */ 
/*      then call this method to actually add it.                       */ 
/************************************************************************/ 
 
/** 
 * Add a geometry to the container. 
 * 
 * Some subclasses of OGRGeometryCollection restrict the types of geometry 
 * that can be added, and may return an error.  The passed geometry is cloned 
 * to make an internal copy. 
 * 
 * There is no SFCOM analog to this method. 
 * 
 * This method is the same as the C function OGR_G_AddGeometry(). 
 * 
 * @param poNewGeom geometry to add to the container. 
 * 
 * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if 
 * the geometry type is illegal for the type of geometry container. 
 */ 
 
OGRErr OGRGeometryCollection::addGeometry( const OGRGeometry * poNewGeom ) 
 
{ 
    OGRGeometry *poClone = poNewGeom->clone(); 
    OGRErr      eErr; 
 
    eErr = addGeometryDirectly( poClone ); 
    if( eErr != OGRERR_NONE ) 
        delete poClone; 
 
    return eErr; 
} 
 
/************************************************************************/ 
/*                        addGeometryDirectly()                         */ 
/*                                                                      */ 
/*      Add a new geometry to a collection.  Subclasses should          */ 
/*      override this to verify the type of the new geometry, and       */ 
/*      then call this method to actually add it.                       */ 
/************************************************************************/ 
 
/** 
 * Add a geometry directly to the container. 
 * 
 * Some subclasses of OGRGeometryCollection restrict the types of geometry 
 * that can be added, and may return an error.  Ownership of the passed 
 * geometry is taken by the container rather than cloning as addGeometry() 
 * does. 
 * 
 * This method is the same as the C function OGR_G_AddGeometryDirectly(). 
 * 
 * There is no SFCOM analog to this method. 
 * 
 * @param poNewGeom geometry to add to the container. 
 * 
 * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if 
 * the geometry type is illegal for the type of geometry container. 
 */ 
 
OGRErr OGRGeometryCollection::addGeometryDirectly( OGRGeometry * poNewGeom ) 
 
{ 
    papoGeoms = (OGRGeometry **) OGRRealloc( papoGeoms, 
                                             sizeof(void*) * (nGeomCount+1) ); 
 
    papoGeoms[nGeomCount] = poNewGeom; 
 
    nGeomCount++; 
 
    if( poNewGeom->getCoordinateDimension() == 3 ) 
        nCoordinateDimension = 3; 
 
    return OGRERR_NONE; 
} 
 
/************************************************************************/ 
/*                           removeGeometry()                           */ 
/************************************************************************/ 
 
/** 
 * Remove a geometry from the container. 
 * 
 * Removing a geometry will cause the geometry count to drop by one, and all 
 * "higher" geometries will shuffle down one in index. 
 * 
 * There is no SFCOM analog to this method. 
 * 
 * This method is the same as the C function OGR_G_RemoveGeometry(). 
 * 
 * @param iGeom the index of the geometry to delete.  A value of -1 is a 
 * special flag meaning that all geometries should be removed. 
 * 
 * @param bDelete if TRUE the geometry will be deallocated, otherwise it will 
 * not.  The default is TRUE as the container is considered to own the 
 * geometries in it.  
 * 
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is 
 * out of range. 
 */ 
 
OGRErr OGRGeometryCollection::removeGeometry( int iGeom, int bDelete ) 
 
{ 
    if( iGeom < -1 || iGeom >= nGeomCount ) 
        return OGRERR_FAILURE; 
 
    // Special case. 
    if( iGeom == -1 ) 
    { 
        while( nGeomCount > 0 ) 
            removeGeometry( nGeomCount-1, bDelete ); 
        return OGRERR_NONE; 
    } 
 
    if( bDelete ) 
        delete papoGeoms[iGeom]; 
 
    memmove( papoGeoms + iGeom, papoGeoms + iGeom + 1,  
             sizeof(void*) * (nGeomCount-iGeom-1) ); 
 
    nGeomCount--; 
 
    return OGRERR_NONE; 
} 
 
/************************************************************************/ 
/*                              WkbSize()                               */ 
/*                                                                      */ 
/*      Return the size of this object in well known binary             */ 
/*      representation including the byte order, and type information.  */ 
/************************************************************************/ 
 
int OGRGeometryCollection::WkbSize() const 
 
{ 
    int         nSize = 9; 
 
    for( int i = 0; i < nGeomCount; i++ ) 
    { 
        nSize += papoGeoms[i]->WkbSize(); 
    } 
 
    return nSize; 
} 
 
/************************************************************************/ 
/*                           importFromWkb()                            */ 
/*                                                                      */ 
/*      Initialize from serialized stream in well known binary          */ 
/*      format.                                                         */ 
/************************************************************************/ 
 
OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, 
                                             int nSize ) 
 
{ 
    OGRwkbByteOrder     eByteOrder; 
    int                 nDataOffset; 
     
    if( nSize < 9 && nSize != -1 ) 
        return OGRERR_NOT_ENOUGH_DATA; 
 
/* -------------------------------------------------------------------- */ 
/*      Get the byte order byte.                                        */ 
/* -------------------------------------------------------------------- */ 
    eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData); 
    CPLAssert( eByteOrder == wkbXDR || eByteOrder == wkbNDR ); 
 
/* -------------------------------------------------------------------- */ 
/*      Get the geometry feature type.  For now we assume that          */ 
/*      geometry type is between 0 and 255 so we only have to fetch     */ 
/*      one byte.                                                       */ 
/* -------------------------------------------------------------------- */ 
#ifdef DEBUG 
    OGRwkbGeometryType eGeometryType; 
 
    if( eByteOrder == wkbNDR ) 
    { 
        eGeometryType = (OGRwkbGeometryType) pabyData[1]; 
    } 
    else 
    { 
        eGeometryType = (OGRwkbGeometryType) pabyData[4]; 
    } 
 
    CPLAssert( eGeometryType == wkbGeometryCollection 
               || eGeometryType == wkbMultiPolygon  
               || eGeometryType == wkbMultiLineString  
               || eGeometryType == wkbMultiPoint ); 
#endif     
 
/* -------------------------------------------------------------------- */ 
/*      Do we already have some existing geometry objects?              */ 
/* -------------------------------------------------------------------- */ 
    if( nGeomCount != 0 ) 
    { 
        for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
            delete papoGeoms[iGeom]; 
 
        OGRFree( papoGeoms ); 
        papoGeoms = NULL; 
    } 
     
/* -------------------------------------------------------------------- */ 
/*      Get the geometry count.                                         */ 
/* -------------------------------------------------------------------- */ 
    memcpy( &nGeomCount, pabyData + 5, 4 ); 
     
    if( OGR_SWAP( eByteOrder ) ) 
        nGeomCount = CPL_SWAP32(nGeomCount); 
 
    papoGeoms = (OGRGeometry **) OGRMalloc(sizeof(void*) * nGeomCount); 
 
    nDataOffset = 9; 
    if( nSize != -1 ) 
        nSize -= nDataOffset; 
 
    nCoordinateDimension = 0; // unknown 
 
/* -------------------------------------------------------------------- */ 
/*      Get the Geoms.                                                  */ 
/* -------------------------------------------------------------------- */ 
    for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    { 
        OGRErr  eErr; 
 
        eErr = OGRGeometryFactory:: 
            createFromWkb( pabyData + nDataOffset, NULL, 
                           papoGeoms + iGeom, nSize ); 
 
        if( eErr != OGRERR_NONE ) 
        { 
            nGeomCount = iGeom; 
            return eErr; 
        } 
 
        if( nSize != -1 ) 
            nSize -= papoGeoms[iGeom]->WkbSize(); 
 
        nDataOffset += papoGeoms[iGeom]->WkbSize(); 
    } 
     
    return OGRERR_NONE; 
} 
 
/************************************************************************/ 
/*                            exportToWkb()                             */ 
/*                                                                      */ 
/*      Build a well known binary representation of this object.        */ 
/************************************************************************/ 
 
OGRErr  OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder, 
                                            unsigned char * pabyData ) const 
 
{ 
    int         nOffset; 
     
/* -------------------------------------------------------------------- */ 
/*      Set the byte order.                                             */ 
/* -------------------------------------------------------------------- */ 
    pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder); 
 
/* -------------------------------------------------------------------- */ 
/*      Set the geometry feature type, ensuring that 3D flag is         */ 
/*      preserved.                                                      */ 
/* -------------------------------------------------------------------- */ 
    GUInt32 nGType = getGeometryType(); 
     
    if( eByteOrder == wkbNDR ) 
        nGType = CPL_LSBWORD32( nGType ); 
    else 
        nGType = CPL_MSBWORD32( nGType ); 
 
    memcpy( pabyData + 1, &nGType, 4 ); 
     
/* -------------------------------------------------------------------- */ 
/*      Copy in the raw data.                                           */ 
/* -------------------------------------------------------------------- */ 
    if( OGR_SWAP( eByteOrder ) ) 
    { 
        int     nCount; 
 
        nCount = CPL_SWAP32( nGeomCount ); 
        memcpy( pabyData+5, &nCount, 4 ); 
    } 
    else 
    { 
        memcpy( pabyData+5, &nGeomCount, 4 ); 
    } 
     
    nOffset = 9; 
     
/* ==================================================================== */ 
/*      Serialize each of the Geoms.                                    */ 
/* ==================================================================== */ 
    for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    { 
        papoGeoms[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset ); 
 
        nOffset += papoGeoms[iGeom]->WkbSize(); 
    } 
     
    return OGRERR_NONE; 
} 
 
/************************************************************************/ 
/*                           importFromWkt()                            */ 
/************************************************************************/ 
 
OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) 
 
{ 
 
    char        szToken[OGR_WKT_TOKEN_MAX]; 
    const char  *pszInput = *ppszInput; 
    int         iGeom; 
 
/* -------------------------------------------------------------------- */ 
/*      Clear existing Geoms.                                           */ 
/* -------------------------------------------------------------------- */ 
    if( nGeomCount > 0 ) 
    { 
        for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
            delete papoGeoms[iGeom]; 
         
        nGeomCount = 0; 
        CPLFree( papoGeoms ); 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Read and verify the type keyword, and ensure it matches the     */ 
/*      actual type of this container.                                  */ 
/* -------------------------------------------------------------------- */ 
    pszInput = OGRWktReadToken( pszInput, szToken ); 
 
    if( !EQUAL(szToken,getGeometryName()) ) 
        return OGRERR_CORRUPT_DATA; 
 
/* -------------------------------------------------------------------- */ 
/*      The next character should be a ( indicating the start of the    */ 
/*      list of objects.                                                */ 
/* -------------------------------------------------------------------- */ 
    pszInput = OGRWktReadToken( pszInput, szToken ); 
    if( szToken[0] != '(' ) 
        return OGRERR_CORRUPT_DATA; 
 
/* -------------------------------------------------------------------- */ 
/*      If the next token is EMPTY, then verify that we have proper     */ 
/*      EMPTY format will a trailing closing bracket.                   */ 
/* -------------------------------------------------------------------- */ 
    OGRWktReadToken( pszInput, szToken ); 
    if( EQUAL(szToken,"EMPTY") ) 
    { 
        pszInput = OGRWktReadToken( pszInput, szToken ); 
        pszInput = OGRWktReadToken( pszInput, szToken ); 
         
        *ppszInput = (char *) pszInput; 
 
        if( !EQUAL(szToken,")") ) 
            return OGRERR_CORRUPT_DATA; 
        else 
            return OGRERR_NONE; 
    } 
 
/* ==================================================================== */ 
/*      Read each subgeometry in turn.                                  */ 
/* ==================================================================== */ 
    do 
    { 
        OGRGeometry *poGeom = NULL; 
        OGRErr      eErr; 
 
        eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput, 
                                                  NULL, &poGeom ); 
        if( eErr != OGRERR_NONE ) 
            return eErr; 
 
        addGeometryDirectly( poGeom ); 
 
/* -------------------------------------------------------------------- */ 
/*      Read the delimeter following the ring.                          */ 
/* -------------------------------------------------------------------- */ 
         
        pszInput = OGRWktReadToken( pszInput, szToken ); 
         
    } while( szToken[0] == ',' ); 
 
/* -------------------------------------------------------------------- */ 
/*      freak if we don't get a closing bracket.                        */ 
/* -------------------------------------------------------------------- */ 
    if( szToken[0] != ')' ) 
        return OGRERR_CORRUPT_DATA; 
     
    *ppszInput = (char *) pszInput; 
     
    return OGRERR_NONE; 
} 
 
/************************************************************************/ 
/*                            exportToWkt()                             */ 
/*                                                                      */ 
/*      Translate this structure into it's well known text format       */ 
/*      equivelent.  This could be made alot more CPU efficient!        */ 
/************************************************************************/ 
 
OGRErr OGRGeometryCollection::exportToWkt( char ** ppszDstText ) const 
 
{ 
    char        **papszGeoms; 
    int         iGeom, nCumulativeLength = 0; 
    OGRErr      eErr; 
 
    if( getNumGeometries() == 0 ) 
    { 
        *ppszDstText = CPLStrdup("GEOMETRYCOLLECTION(EMPTY)"); 
        return OGRERR_NONE; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Build a list of strings containing the stuff for each Geom.     */ 
/* -------------------------------------------------------------------- */ 
    papszGeoms = (char **) CPLCalloc(sizeof(char *),nGeomCount); 
 
    for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    { 
        eErr = papoGeoms[iGeom]->exportToWkt( &(papszGeoms[iGeom]) ); 
        if( eErr != OGRERR_NONE ) 
            return eErr; 
 
        nCumulativeLength += strlen(papszGeoms[iGeom]); 
    } 
     
/* -------------------------------------------------------------------- */ 
/*      Allocate the right amount of space for the aggregated string    */ 
/* -------------------------------------------------------------------- */ 
    *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nGeomCount + 23); 
 
    if( *ppszDstText == NULL ) 
        return OGRERR_NOT_ENOUGH_MEMORY; 
 
/* -------------------------------------------------------------------- */ 
/*      Build up the string, freeing temporary strings as we go.        */ 
/* -------------------------------------------------------------------- */ 
    strcpy( *ppszDstText, getGeometryName() ); 
    strcat( *ppszDstText, " (" ); 
 
    for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    {                                                            
        if( iGeom > 0 ) 
            strcat( *ppszDstText, "," ); 
         
        strcat( *ppszDstText, papszGeoms[iGeom] ); 
        VSIFree( papszGeoms[iGeom] ); 
    } 
 
    strcat( *ppszDstText, ")" ); 
 
    CPLFree( papszGeoms ); 
 
    return OGRERR_NONE; 
} 
 
/************************************************************************/ 
/*                            getEnvelope()                             */ 
/************************************************************************/ 
 
void OGRGeometryCollection::getEnvelope( OGREnvelope * psEnvelope ) const 
 
{ 
    OGREnvelope         oGeomEnv; 
     
    if( nGeomCount == 0 ) 
        return; 
 
    papoGeoms[0]->getEnvelope( psEnvelope ); 
 
    for( int iGeom = 1; iGeom < nGeomCount; iGeom++ ) 
    { 
        papoGeoms[iGeom]->getEnvelope( &oGeomEnv ); 
 
        if( psEnvelope->MinX > oGeomEnv.MinX ) 
            psEnvelope->MinX = oGeomEnv.MinX; 
        if( psEnvelope->MinY > oGeomEnv.MinY ) 
            psEnvelope->MinY = oGeomEnv.MinY; 
        if( psEnvelope->MaxX < oGeomEnv.MaxX ) 
            psEnvelope->MaxX = oGeomEnv.MaxX; 
        if( psEnvelope->MaxY < oGeomEnv.MaxY ) 
            psEnvelope->MaxY = oGeomEnv.MaxY; 
    } 
} 
 
/************************************************************************/ 
/*                               Equals()                               */ 
/************************************************************************/ 
 
OGRBoolean OGRGeometryCollection::Equals( OGRGeometry * poOther ) const 
 
{ 
    OGRGeometryCollection *poOGC = (OGRGeometryCollection *) poOther; 
 
    if( poOGC == this ) 
        return TRUE; 
     
    if( poOther->getGeometryType() != getGeometryType() ) 
        return FALSE; 
 
    if( getNumGeometries() != poOGC->getNumGeometries() ) 
        return FALSE; 
     
    // we should eventually test the SRS. 
 
    for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    { 
        if( !getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)) ) 
            return FALSE; 
    } 
 
    return TRUE; 
} 
 
/************************************************************************/ 
/*                             transform()                              */ 
/************************************************************************/ 
 
OGRErr OGRGeometryCollection::transform( OGRCoordinateTransformation *poCT ) 
 
{ 
#ifdef DISABLE_OGRGEOM_TRANSFORM 
    return OGRERR_FAILURE; 
#else 
    for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    { 
        OGRErr  eErr; 
 
        eErr = papoGeoms[iGeom]->transform( poCT ); 
        if( eErr != OGRERR_NONE ) 
        { 
            if( iGeom != 0 ) 
            { 
                CPLDebug("OGR",  
                         "OGRGeometryCollection::transform() failed for a geometry other\n" 
                         "than the first, meaning some geometries are transformed\n" 
                         "and some are not!\n" ); 
 
                return OGRERR_FAILURE; 
            } 
 
            return eErr; 
        } 
    } 
 
    assignSpatialReference( poCT->GetTargetCS() ); 
 
    return OGRERR_NONE; 
#endif 
} 
 
/************************************************************************/ 
/*                             closeRings()                             */ 
/************************************************************************/ 
 
void OGRGeometryCollection::closeRings() 
 
{ 
    for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) 
    { 
        if( wkbFlatten(papoGeoms[iGeom]->getGeometryType()) == wkbPolygon ) 
            ((OGRPolygon *) papoGeoms[iGeom])->closeRings(); 
    } 
}