CPLXMLNode* OGR_G_ExportEnvelopeToKMLTree( OGRGeometryH hGeometry )
{
    VALIDATE_POINTER1( hGeometry, "OGR_G_ExportEnvelopeToKMLTree", NULL );

    CPLXMLNode* psBox = NULL;
    CPLXMLNode* psCoord = NULL;
    OGREnvelope sEnvelope;
    char szCoordinate[256] = { 0 };
    char* pszY = NULL;

    memset( &sEnvelope, 0, sizeof(sEnvelope) );
    ((OGRGeometry*)(hGeometry))->getEnvelope( &sEnvelope );

    if( sEnvelope.MinX == 0 && sEnvelope.MaxX == 0
            && sEnvelope.MaxX == 0 && sEnvelope.MaxY == 0 )
    {
        /* there is apparently a special way of representing a null box
           geometry ... we should use it here eventually. */

        return NULL;
    }

    psBox = CPLCreateXMLNode( NULL, CXT_Element, "Box" );

    /* -------------------------------------------------------------------- */
    /*      Add minxy coordinate.                                           */
    /* -------------------------------------------------------------------- */
    psCoord = CPLCreateXMLNode( psBox, CXT_Element, "coord" );

    MakeKMLCoordinate( szCoordinate, sEnvelope.MinX, sEnvelope.MinY, 0.0,
                       FALSE );
    pszY = strstr(szCoordinate,",") + 1;
    pszY[-1] = '\0';

    CPLCreateXMLElementAndValue( psCoord, "X", szCoordinate );
    CPLCreateXMLElementAndValue( psCoord, "Y", pszY );

    /* -------------------------------------------------------------------- */
    /*      Add maxxy coordinate.                                           */
    /* -------------------------------------------------------------------- */
    psCoord = CPLCreateXMLNode( psBox, CXT_Element, "coord" );

    MakeKMLCoordinate( szCoordinate, sEnvelope.MaxX, sEnvelope.MaxY, 0.0,
                       FALSE );
    pszY = strstr(szCoordinate,",") + 1;
    pszY[-1] = '\0';

    CPLCreateXMLElementAndValue( psCoord, "X", szCoordinate );
    CPLCreateXMLElementAndValue( psCoord, "Y", pszY );

    return psBox;
}
static void AppendCoordinateList( OGRLineString *poLine,
                                  char **ppszText, size_t *pnLength,
                                  size_t *pnMaxLength )

{
    char szCoordinate[256]= { 0 };
    const bool b3D = CPL_TO_BOOL(wkbHasZ(poLine->getGeometryType()));

    *pnLength += strlen(*ppszText + *pnLength);
    _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength );

    strcat( *ppszText + *pnLength, "<coordinates>" );
    *pnLength += strlen(*ppszText + *pnLength);

    for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ )
    {
        MakeKMLCoordinate( szCoordinate, sizeof(szCoordinate),
                           poLine->getX(iPoint),
                           poLine->getY(iPoint),
                           poLine->getZ(iPoint),
                           b3D );
        _GrowBuffer( *pnLength + strlen(szCoordinate)+1,
            ppszText, pnMaxLength );

        if( iPoint != 0 )
            strcat( *ppszText + *pnLength, " " );

        strcat( *ppszText + *pnLength, szCoordinate );
        *pnLength += strlen(*ppszText + *pnLength);
    }

    _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength );
    strcat( *ppszText + *pnLength, "</coordinates>" );
    *pnLength += strlen(*ppszText + *pnLength);
}
static void AppendCoordinateList( OGRLineString *poLine, 
                                  char **ppszText, int *pnLength, 
                                  int *pnMaxLength )

{
    char szCoordinate[256]= { 0 };
    int b3D = (poLine->getGeometryType() & wkb25DBit);

    *pnLength += strlen(*ppszText + *pnLength);
    _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength );

    strcat( *ppszText + *pnLength, "<coordinates>" );
    *pnLength += strlen(*ppszText + *pnLength);

    for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ )
    {
        MakeKMLCoordinate( szCoordinate, 
                           poLine->getX(iPoint),
                           poLine->getY(iPoint),
                           poLine->getZ(iPoint),
                           b3D );
        _GrowBuffer( *pnLength + strlen(szCoordinate)+1, 
            ppszText, pnMaxLength );

        if( iPoint != 0 )
            strcat( *ppszText + *pnLength, " " );

        strcat( *ppszText + *pnLength, szCoordinate );
        *pnLength += strlen(*ppszText + *pnLength);
    }

    _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength );
    strcat( *ppszText + *pnLength, "</coordinates>" );
    *pnLength += strlen(*ppszText + *pnLength);
}
static bool OGR2KMLGeometryAppend( OGRGeometry *poGeometry,
                                   char **ppszText, size_t *pnLength,
                                   size_t *pnMaxLength, char *szAltitudeMode )

{
/* -------------------------------------------------------------------- */
/*      2D Point                                                        */
/* -------------------------------------------------------------------- */
    if( poGeometry->getGeometryType() == wkbPoint )
    {
        OGRPoint* poPoint = static_cast<OGRPoint*>(poGeometry);

        if (poPoint->getCoordinateDimension() == 0)
        {
            _GrowBuffer( *pnLength + 10,
                     ppszText, pnMaxLength );
            strcat( *ppszText + *pnLength, "<Point/>");
            *pnLength += strlen( *ppszText + *pnLength );
        }
        else
        {
            char szCoordinate[256] = { 0 };
            MakeKMLCoordinate( szCoordinate, sizeof(szCoordinate),
                            poPoint->getX(), poPoint->getY(), 0.0, FALSE );

            _GrowBuffer( *pnLength + strlen(szCoordinate) + 60,
                         ppszText, pnMaxLength );

            snprintf( *ppszText + *pnLength, *pnMaxLength - *pnLength,
                      "<Point><coordinates>%s</coordinates></Point>",
                      szCoordinate );

            *pnLength += strlen( *ppszText + *pnLength );
        }
    }
/* -------------------------------------------------------------------- */
/*      3D Point                                                        */
/* -------------------------------------------------------------------- */
    else if( poGeometry->getGeometryType() == wkbPoint25D )
    {
        char szCoordinate[256] = { 0 };
        OGRPoint *poPoint = static_cast<OGRPoint*>(poGeometry);

        MakeKMLCoordinate( szCoordinate, sizeof(szCoordinate),
                           poPoint->getX(), poPoint->getY(), poPoint->getZ(),
                           true );

        if (NULL == szAltitudeMode)
        {
            _GrowBuffer( *pnLength + strlen(szCoordinate) + 70,
                         ppszText, pnMaxLength );

            snprintf( *ppszText + *pnLength, *pnMaxLength - *pnLength,
                      "<Point><coordinates>%s</coordinates></Point>",
                      szCoordinate );
        }
        else
        {
            _GrowBuffer( *pnLength + strlen(szCoordinate)
                         + strlen(szAltitudeMode) + 70,
                         ppszText, pnMaxLength );

            snprintf( *ppszText + *pnLength, *pnMaxLength - *pnLength,
                      "<Point>%s<coordinates>%s</coordinates></Point>",
                      szAltitudeMode, szCoordinate );
        }

        *pnLength += strlen( *ppszText + *pnLength );
    }
/* -------------------------------------------------------------------- */
/*      LineString and LinearRing                                       */
/* -------------------------------------------------------------------- */
    else if( poGeometry->getGeometryType() == wkbLineString
             || poGeometry->getGeometryType() == wkbLineString25D )
    {
        const bool bRing = EQUAL(poGeometry->getGeometryName(),"LINEARRING");

        if( bRing )
            AppendString( ppszText, pnLength, pnMaxLength,
                          "<LinearRing>" );
        else
            AppendString( ppszText, pnLength, pnMaxLength,
                          "<LineString>" );

        if (NULL != szAltitudeMode)
        {
            AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode);
        }

        AppendCoordinateList( reinterpret_cast<OGRLineString *>(poGeometry),
                              ppszText, pnLength, pnMaxLength );

        if( bRing )
            AppendString( ppszText, pnLength, pnMaxLength,
                          "</LinearRing>" );
        else
            AppendString( ppszText, pnLength, pnMaxLength,
                          "</LineString>" );
    }

/* -------------------------------------------------------------------- */
/*      Polygon                                                         */
/* -------------------------------------------------------------------- */
    else if( poGeometry->getGeometryType() == wkbPolygon
             || poGeometry->getGeometryType() == wkbPolygon25D )
    {
        OGRPolygon* poPolygon = static_cast<OGRPolygon*>(poGeometry);

        AppendString( ppszText, pnLength, pnMaxLength, "<Polygon>" );

        if (NULL != szAltitudeMode)
        {
            AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode);
        }

        if( poPolygon->getExteriorRing() != NULL )
        {
            AppendString( ppszText, pnLength, pnMaxLength,
                          "<outerBoundaryIs>" );

            if( !OGR2KMLGeometryAppend( poPolygon->getExteriorRing(),
                                        ppszText, pnLength, pnMaxLength,
                                        szAltitudeMode ) )
            {
                return false;
            }
            AppendString( ppszText, pnLength, pnMaxLength,
                          "</outerBoundaryIs>" );
        }

        for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ )
        {
            OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing);

            AppendString( ppszText, pnLength, pnMaxLength,
                          "<innerBoundaryIs>" );

            if( !OGR2KMLGeometryAppend( poRing, ppszText, pnLength,
                                        pnMaxLength, szAltitudeMode ) )
            {
                return false;
            }
            AppendString( ppszText, pnLength, pnMaxLength,
                          "</innerBoundaryIs>" );
        }

        AppendString( ppszText, pnLength, pnMaxLength,
                      "</Polygon>" );
    }

/* -------------------------------------------------------------------- */
/*      MultiPolygon                                                    */
/* -------------------------------------------------------------------- */
    else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon
             || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString
             || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint
             || wkbFlatten(poGeometry->getGeometryType()) ==
                wkbGeometryCollection )
    {
        OGRGeometryCollection* poGC = NULL;
        poGC = static_cast<OGRGeometryCollection*>(poGeometry);

        AppendString( ppszText, pnLength, pnMaxLength, "<MultiGeometry>" );

        // XXX - mloskot
        //if (NULL != szAltitudeMode)
        //{
        //    AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode);
        //}

        for( int iMember = 0; iMember < poGC->getNumGeometries(); iMember++)
        {
            OGRGeometry *poMember = poGC->getGeometryRef( iMember );

            if( !OGR2KMLGeometryAppend( poMember, ppszText, pnLength,
                                        pnMaxLength, szAltitudeMode ) )
            {
                return false;
            }
        }

        AppendString( ppszText, pnLength, pnMaxLength, "</MultiGeometry>" );
    }
    else
    {
        return false;
    }

    return true;
}