www.pudn.com > mitab-1.5.1.zip > mitab_coordsys.cpp


/********************************************************************** 
 * $Id: mitab_coordsys.cpp,v 1.34 2006/03/10 19:50:45 fwarmerdam Exp $ 
 * 
 * Name:     mitab_coordsys.cpp 
 * Project:  MapInfo TAB Read/Write library 
 * Language: C++ 
 * Purpose:  Implementation translation between MIF CoordSys format, and 
 *           and OGRSpatialRef format. 
 * Author:   Frank Warmerdam, warmerdam@pobox.com 
 * 
 ********************************************************************** 
 * Copyright (c) 1999-2001, 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: mitab_coordsys.cpp,v $ 
 * Revision 1.34  2006/03/10 19:50:45  fwarmerdam 
 * Coordsys false easting and northing are in the units of the coordsys, not 
 * necessarily meters.  Adjusted mitab_coordsys.cpp to reflect this. 
 * http://bugzilla.remotesensing.org/show_bug.cgi?id=1113 
 * 
 * Revision 1.33  2005/09/29 20:13:57  dmorissette 
 * MITABCoordSys2SpatialRef() patches from Anthony D (bug 1155): 
 * Improved support for modified TM projections 21-24. 
 * Added support for affine parameters (inside #ifdef MITAB_AFFINE_PARAMS since 
 * affine params cannot be stored directly in OGRSpatialReference) 
 * 
 * Revision 1.32  2005/08/07 21:00:38  fwarmerdam 
 * Initialize adfDatumParm[] to avoid warnings with gcc 4. 
 * 
 * Revision 1.31  2005/05/12 22:07:52  dmorissette 
 * Improved handling of Danish modified TM proj#21-24 (hss, bugs 976,1010) 
 * 
 * Revision 1.30  2005/03/22 23:24:54  dmorissette 
 * Added support for datum id in .MAP header (bug 910) 
 * 
 * Revision 1.29  2004/06/03 19:36:53  fwarmerdam 
 * fixed memory leak processing non-earth coordsys 
 * 
 * Revision 1.28  2003/03/21 14:20:42  warmerda 
 * fixed up regional mercator handling, was screwing up transverse mercator 
 * 
 * Revision 1.27  2003/01/09 17:33:26  warmerda 
 * fixed ellipsoid extraction for datum 999/9999 
 * 
 * Revision 1.26  2002/12/12 20:12:18  warmerda 
 * fixed signs of rotational parameters for TOWGS84 in WKT 
 * 
 * Revision 1.25  2002/10/15 14:33:30  warmerda 
 * Added untested support in mitab_spatialref.cpp, and mitab_coordsys.cpp for 
 * projections Regional Mercator (26), Polyconic (27), Azimuthal Equidistant - 
 * All origin latitudes (28), and Lambert Azimuthal Equal Area - any aspect (29). 
 * 
 * Revision 1.24  2002/09/23 13:16:04  warmerda 
 * fixed leak in MITABExtractCoordSysBounds() 
 * 
 * Revision 1.23  2002/04/01 19:49:24  warmerda 
 * added support for cassini/soldner - proj 30 
 * 
 * Revision 1.22  2002/03/01 19:00:15  warmerda 
 * False Easting/Northing should be in the linear units of measure in MapInfo, 
 * but in OGRSpatialReference/WKT they are always in meters.  Convert accordingly. 
 * 
 * Revision 1.21  2001/04/04 21:43:19  warmerda 
 * added code to set WGS84 values 
 * 
 * Revision 1.20  2001/01/23 21:23:42  daniel 
 * Added projection bounds lookup table, called from TABFile::SetProjInfo() 
 * 
 * Revision 1.19  2001/01/22 16:00:53  warmerda 
 * reworked swiss projection support 
 * 
 * Revision 1.18  2001/01/19 21:56:18  warmerda 
 * added untested support for Swiss Oblique Mercator 
 **********************************************************************/ 
 
#include "mitab.h" 
#include "mitab_utils.h" 
 
extern MapInfoDatumInfo asDatumInfoList[200]; 
extern MapInfoSpheroidInfo asSpheroidInfoList[200]; 
 
/************************************************************************/ 
/*                             GetMIFParm()                             */ 
/************************************************************************/ 
 
static double GetMIFParm( char ** papszFields, int iField, double dfDefault ) 
 
{ 
    if( iField >= CSLCount(papszFields) ) 
        return dfDefault; 
    else 
        return atof(papszFields[iField]); 
} 
 
/************************************************************************/ 
/*                      MITABCoordSys2SpatialRef()                      */ 
/*                                                                      */ 
/*      Convert a MIF COORDSYS string into a new OGRSpatialReference    */ 
/*      object.                                                         */ 
/************************************************************************/ 
 
OGRSpatialReference *MITABCoordSys2SpatialRef( const char * pszCoordSys ) 
 
{ 
    char        **papszFields; 
    OGRSpatialReference *poSR; 
 
#ifdef MITAB_AFFINE_PARAMS  // See MITAB bug 1155 
    // Encom 2003 
    int nAffineUnits = 7; 
    double dAffineParams[6]; 
#endif 
 
    if( pszCoordSys == NULL ) 
        return NULL; 
     
/* -------------------------------------------------------------------- */ 
/*      Parse the passed string into words.                             */ 
/* -------------------------------------------------------------------- */ 
    while(*pszCoordSys == ' ') pszCoordSys++;  // Eat leading spaces 
    if( EQUALN(pszCoordSys,"CoordSys",8) ) 
        pszCoordSys += 9; 
     
    papszFields = CSLTokenizeStringComplex( pszCoordSys, " ,", TRUE, FALSE ); 
 
#ifdef MITAB_AFFINE_PARAMS  // See MITAB bug 1155 
/* -------------------------------------------------------------------- */ 
/*      Store and then clip off Affine information. - Encom 2003        */ 
/* -------------------------------------------------------------------- */ 
    int         iAffine = CSLFindString( papszFields, "Affine" ); 
    int         nAffineIndex = 0; 
    int         nAffineFlag = 0; 
 
    while( iAffine != -1 && papszFields[iAffine] != NULL ) 
    { 
        nAffineFlag = 1; 
        if (nAffineIndex<2) 
        { 
            // Ignore "Affine Units" 
        } 
        else if (nAffineIndex==2) 
        { 
            // Convert units to integer (TBD) 
            nAffineUnits = TABUnitIdFromString(papszFields[iAffine]); 
            if (nAffineUnits==-1) nAffineUnits = 7; // metres is default 
        } 
        else if (nAffineIndex<=8) 
        { 
            // Store affine params 
            dAffineParams[nAffineIndex-3] = atof(papszFields[iAffine]); 
        } 
        nAffineIndex++; 
 
        CPLFree( papszFields[iAffine] ); 
        papszFields[iAffine] = NULL; 
        iAffine++; 
    } 
#endif // MITAB_AFFINE_PARAMS 
 
/* -------------------------------------------------------------------- */ 
/*      Clip off Bounds information.                                    */ 
/* -------------------------------------------------------------------- */ 
    int         iBounds = CSLFindString( papszFields, "Bounds" ); 
 
    while( iBounds != -1 && papszFields[iBounds] != NULL ) 
    { 
        CPLFree( papszFields[iBounds] ); 
        papszFields[iBounds] = NULL; 
        iBounds++; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Create a spatialreference object to operate on.                 */ 
/* -------------------------------------------------------------------- */ 
    poSR = new OGRSpatialReference; 
 
#ifdef MITAB_AFFINE_PARAMS  // See MITAB bug 1155 
    // Encom 2003 
    if (nAffineFlag) 
    { 
        poSR->nAffineFlag = 1; 
        poSR->nAffineUnit = nAffineUnits; 
        poSR->dAffineParamA = dAffineParams[0]; 
        poSR->dAffineParamB = dAffineParams[1]; 
        poSR->dAffineParamC = dAffineParams[2]; 
        poSR->dAffineParamD = dAffineParams[3]; 
        poSR->dAffineParamE = dAffineParams[4]; 
        poSR->dAffineParamF = dAffineParams[5]; 
    } 
    else 
    { 
        poSR->nAffineFlag = 0; // Encom 2005 
    } 
#endif 
 
/* -------------------------------------------------------------------- */ 
/*      Fetch the projection.                                           */ 
/* -------------------------------------------------------------------- */ 
    char        **papszNextField; 
    int nProjection = 0; 
 
    if( CSLCount( papszFields ) >= 3 
        && EQUAL(papszFields[0],"Earth") 
        && EQUAL(papszFields[1],"Projection") ) 
    { 
        nProjection = atoi(papszFields[2]); 
        papszNextField = papszFields + 3; 
    } 
    else if (CSLCount( papszFields ) >= 2 
             && EQUAL(papszFields[0],"NonEarth") ) 
    { 
        // NonEarth Units "..." Bounds (x, y) (x, y) 
        nProjection = 0; 
        papszNextField = papszFields + 2; 
 
        if( papszNextField[0] != NULL && EQUAL(papszNextField[0],"Units") ) 
            papszNextField++; 
    } 
    else 
    { 
        // Invalid projection string ??? 
        if (CSLCount(papszFields) > 0) 
            CPLError(CE_Warning, CPLE_IllegalArg, 
                     "Failed parsing CoordSys: '%s'", pszCoordSys); 
        CSLDestroy(papszFields); 
        return NULL; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Fetch the datum information.                                    */ 
/* -------------------------------------------------------------------- */ 
    int         nDatum = 0; 
    double      adfDatumParm[8] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; 
    int         nEllipsoid=0; 
 
    if( nProjection != 0 && CSLCount(papszNextField) > 0 ) 
    { 
        nDatum = atoi(papszNextField[0]); 
        papszNextField++; 
    } 
 
    if( (nDatum == 999 || nDatum == 9999) 
        && CSLCount(papszNextField) >= 4 ) 
    { 
        nEllipsoid = atoi(papszNextField[0]); 
        adfDatumParm[0] = atof(papszNextField[1]); 
        adfDatumParm[1] = atof(papszNextField[2]); 
        adfDatumParm[2] = atof(papszNextField[3]); 
        papszNextField += 4; 
    } 
 
    if( nDatum == 9999 
        && CSLCount(papszNextField) >= 5 ) 
    { 
        adfDatumParm[3] = atof(papszNextField[0]); 
        adfDatumParm[4] = atof(papszNextField[1]); 
        adfDatumParm[5] = atof(papszNextField[2]); 
        adfDatumParm[6] = atof(papszNextField[3]); 
        adfDatumParm[7] = atof(papszNextField[4]); 
        papszNextField += 5; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Fetch the units string.                                         */ 
/* -------------------------------------------------------------------- */ 
    const char  *pszMIFUnits = NULL; 
    const char  *pszUnitsName = NULL; 
    double dfUnitsConv = 1.0; 
     
    if( CSLCount(papszNextField) > 0 ) 
    { 
        pszMIFUnits = papszNextField[0]; 
        papszNextField++; 
    } 
 
    if( nProjection == 1 || pszMIFUnits == NULL ) 
        /* do nothing */; 
    else if( EQUAL(pszMIFUnits,"km") ) 
    { 
        pszUnitsName = "Kilometer";  
        dfUnitsConv = 1000.0; 
    } 
    else if( EQUAL(pszMIFUnits, "in" ) ) 
    { 
        pszUnitsName = "IINCH";  
        dfUnitsConv = 0.0254;  
    } 
    else if( EQUAL(pszMIFUnits, "ft" ) ) 
    { 
        pszUnitsName = SRS_UL_FOOT; 
        dfUnitsConv = atof(SRS_UL_FOOT_CONV); 
    } 
    else if( EQUAL(pszMIFUnits, "yd" ) ) 
    { 
        pszUnitsName = "IYARD"; 
        dfUnitsConv = 0.9144; 
    } 
    else if( EQUAL(pszMIFUnits, "mm" ) ) 
    { 
        pszUnitsName = "Millimeter"; 
        dfUnitsConv = 0.001; 
    } 
    else if( EQUAL(pszMIFUnits, "cm" ) ) 
    { 
        pszUnitsName = "Centimeter"; 
        dfUnitsConv = 0.01; 
    } 
    else if( EQUAL(pszMIFUnits, "m" ) ) 
    { 
        pszUnitsName = SRS_UL_METER; 
        dfUnitsConv = 1.0; 
    }    
    else if( EQUAL(pszMIFUnits, "survey foot" ) 
             || EQUAL(pszMIFUnits, "survey ft" ) ) 
    { 
        pszUnitsName = SRS_UL_US_FOOT; 
        dfUnitsConv = atof(SRS_UL_US_FOOT_CONV); 
    }    
    else if( EQUAL(pszMIFUnits, "nmi" ) ) 
    { 
        pszUnitsName = SRS_UL_NAUTICAL_MILE; 
        dfUnitsConv = atof(SRS_UL_NAUTICAL_MILE_CONV); 
    }    
    else if( EQUAL(pszMIFUnits, "li" ) ) 
    { 
        pszUnitsName = SRS_UL_LINK; 
        dfUnitsConv = atof(SRS_UL_LINK_CONV); 
    } 
    else if( EQUAL(pszMIFUnits, "ch" ) ) 
    { 
        pszUnitsName = SRS_UL_CHAIN; 
        dfUnitsConv = atof(SRS_UL_CHAIN_CONV); 
    }    
    else if( EQUAL(pszMIFUnits, "rd" ) ) 
    { 
        pszUnitsName = SRS_UL_ROD; 
        dfUnitsConv = atof(SRS_UL_ROD); 
    }    
    else if( EQUAL(pszMIFUnits, "mi" ) ) 
    { 
        pszUnitsName = "Mile"; 
        dfUnitsConv = 1609.344; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Handle the PROJCS style projections, but add the datum          */ 
/*      later.                                                          */ 
/*                                                                      */ 
/*      Note that per GDAL bug 1113 the false easting and north are     */ 
/*      in local units, not necessarily meters.                         */ 
/* -------------------------------------------------------------------- */ 
    switch( nProjection ) 
    { 
        /*-------------------------------------------------------------- 
         * NonEarth ... we return with an empty SpatialRef.  Eventually 
         * we might want to include the units, but not for now. 
         * 
         * __TODO__ Changed to return NULL because returning an empty 
         * SpatialRef caused confusion between Latlon and NonEarth since 
         * empty SpatialRefs do have a GEOGCS set and makes them look like 
         * Lat/Lon SpatialRefs. 
         * 
         * Ideally we would like to return a SpatialRef whith no GEGOCS 
         *-------------------------------------------------------------*/ 
      case 0: 
        poSR->SetLocalCS( "Nonearth" ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * lat/long .. just add the GEOGCS later. 
         *-------------------------------------------------------------*/ 
      case 1: 
        break; 
 
        /*-------------------------------------------------------------- 
         * Cylindrical Equal Area 
         *-------------------------------------------------------------*/ 
      case 2: 
        poSR->SetCEA( GetMIFParm( papszNextField, 1, 0.0 ), 
                      GetMIFParm( papszNextField, 0, 0.0 ), 
                      GetMIFParm( papszNextField, 2, 0.0 ), 
                      GetMIFParm( papszNextField, 3, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Lambert Conic Conformal 
         *-------------------------------------------------------------*/ 
      case 3: 
        poSR->SetLCC( GetMIFParm( papszNextField, 2, 0.0 ), 
                      GetMIFParm( papszNextField, 3, 0.0 ), 
                      GetMIFParm( papszNextField, 1, 0.0 ), 
                      GetMIFParm( papszNextField, 0, 0.0 ), 
                      GetMIFParm( papszNextField, 4, 0.0 ), 
                      GetMIFParm( papszNextField, 5, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Lambert Azimuthal Equal Area 
         *-------------------------------------------------------------*/ 
      case 4:  
      case 29: 
        poSR->SetLAEA( GetMIFParm( papszNextField, 1, 0.0 ), 
                       GetMIFParm( papszNextField, 0, 0.0 ), 
                       0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Azimuthal Equidistant  
         *-------------------------------------------------------------*/ 
      case 5:  /* polar aspect only */ 
      case 28: /* all aspects */ 
        poSR->SetAE( GetMIFParm( papszNextField, 1, 0.0 ), 
                     GetMIFParm( papszNextField, 0, 0.0 ), 
                     0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Equidistant Conic 
         *-------------------------------------------------------------*/ 
      case 6: 
        poSR->SetEC( GetMIFParm( papszNextField, 2, 0.0 ), 
                     GetMIFParm( papszNextField, 3, 0.0 ), 
                     GetMIFParm( papszNextField, 1, 0.0 ), 
                     GetMIFParm( papszNextField, 0, 0.0 ), 
                     GetMIFParm( papszNextField, 4, 0.0 ), 
                     GetMIFParm( papszNextField, 5, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Hotine Oblique Mercator 
         *-------------------------------------------------------------*/ 
      case 7: 
        poSR->SetHOM( GetMIFParm( papszNextField, 1, 0.0 ), 
                      GetMIFParm( papszNextField, 0, 0.0 ), 
                      GetMIFParm( papszNextField, 2, 0.0 ), 
                      90.0, 
                      GetMIFParm( papszNextField, 3, 1.0 ), 
                      GetMIFParm( papszNextField, 4, 0.0 ), 
                      GetMIFParm( papszNextField, 5, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Transverse Mercator 
         *-------------------------------------------------------------*/ 
      case 8: 
        poSR->SetTM( GetMIFParm( papszNextField, 1, 0.0 ), 
                     GetMIFParm( papszNextField, 0, 0.0 ), 
                     GetMIFParm( papszNextField, 2, 1.0 ), 
                     GetMIFParm( papszNextField, 3, 0.0 ), 
                     GetMIFParm( papszNextField, 4, 0.0 ) ); 
        break; 
 
        /*---------------------------------------------------------------- 
         * Transverse Mercator,(modified for Danish System 34 Jylland-Fyn) 
         *---------------------------------------------------------------*/ 
      case 21: 
        poSR->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_21, 
                            GetMIFParm( papszNextField, 1, 0.0 ), 
                            GetMIFParm( papszNextField, 0, 0.0 ), 
                            GetMIFParm( papszNextField, 2, 1.0 ), 
                            GetMIFParm( papszNextField, 3, 0.0 ), 
                            GetMIFParm( papszNextField, 4, 0.0 )); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Transverse Mercator,(modified for Danish System 34 Sjaelland) 
         *-------------------------------------------------------------*/ 
      case 22: 
        poSR->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_22, 
                            GetMIFParm( papszNextField, 1, 0.0 ), 
                            GetMIFParm( papszNextField, 0, 0.0 ), 
                            GetMIFParm( papszNextField, 2, 1.0 ), 
                            GetMIFParm( papszNextField, 3, 0.0 ), 
                            GetMIFParm( papszNextField, 4, 0.0 )); 
        break; 
 
        /*---------------------------------------------------------------- 
         * Transverse Mercator,(modified for Danish System 34/45 Bornholm) 
         *---------------------------------------------------------------*/ 
      case 23: 
        poSR->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_23, 
                            GetMIFParm( papszNextField, 1, 0.0 ), 
                            GetMIFParm( papszNextField, 0, 0.0 ), 
                            GetMIFParm( papszNextField, 2, 1.0 ), 
                            GetMIFParm( papszNextField, 3, 0.0 ), 
                            GetMIFParm( papszNextField, 4, 0.0 )); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Transverse Mercator,(modified for Finnish KKJ) 
         *-------------------------------------------------------------*/ 
      case 24: 
        poSR->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_24, 
                            GetMIFParm( papszNextField, 1, 0.0 ), 
                            GetMIFParm( papszNextField, 0, 0.0 ), 
                            GetMIFParm( papszNextField, 2, 1.0 ), 
                            GetMIFParm( papszNextField, 3, 0.0 ), 
                            GetMIFParm( papszNextField, 4, 0.0 )); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Albers Conic Equal Area 
         *-------------------------------------------------------------*/ 
      case 9: 
        poSR->SetACEA( GetMIFParm( papszNextField, 2, 0.0 ), 
                       GetMIFParm( papszNextField, 3, 0.0 ), 
                       GetMIFParm( papszNextField, 1, 0.0 ), 
                       GetMIFParm( papszNextField, 0, 0.0 ), 
                       GetMIFParm( papszNextField, 4, 0.0 ), 
                       GetMIFParm( papszNextField, 5, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Mercator 
         *-------------------------------------------------------------*/ 
      case 10: 
        poSR->SetMercator( 0.0, GetMIFParm( papszNextField, 0, 0.0 ), 
                           1.0, 0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Miller Cylindrical 
         *-------------------------------------------------------------*/ 
      case 11: 
        poSR->SetMC( 0.0, GetMIFParm( papszNextField, 0, 0.0 ), 
                     0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Robinson 
         *-------------------------------------------------------------*/ 
      case 12: 
        poSR->SetRobinson( GetMIFParm( papszNextField, 0, 0.0 ), 
                           0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Mollweide 
         *-------------------------------------------------------------*/ 
      case 13: 
        poSR->SetMollweide( GetMIFParm( papszNextField, 0, 0.0 ), 
                            0.0, 0.0 ); 
 
        /*-------------------------------------------------------------- 
         * Eckert IV 
         *-------------------------------------------------------------*/ 
      case 14: 
        poSR->SetEckertIV( GetMIFParm( papszNextField, 0, 0.0 ), 
                           0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Eckert VI 
         *-------------------------------------------------------------*/ 
      case 15: 
        poSR->SetEckertVI( GetMIFParm( papszNextField, 0, 0.0 ), 
                           0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Sinusoidal 
         *-------------------------------------------------------------*/ 
      case 16: 
        poSR->SetSinusoidal( GetMIFParm( papszNextField, 0, 0.0 ), 
                             0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Gall 
         *-------------------------------------------------------------*/ 
      case 17: 
        poSR->SetGS( GetMIFParm( papszNextField, 0, 0.0 ), 
                     0.0, 0.0 ); 
        break; 
         
        /*-------------------------------------------------------------- 
         * New Zealand Map Grid 
         *-------------------------------------------------------------*/ 
      case 18: 
        poSR->SetNZMG( GetMIFParm( papszNextField, 1, 0.0 ), 
                       GetMIFParm( papszNextField, 0, 0.0 ), 
                       GetMIFParm( papszNextField, 2, 0.0 ), 
                       GetMIFParm( papszNextField, 3, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Lambert Conic Conformal (Belgium) 
         *-------------------------------------------------------------*/ 
      case 19: 
        poSR->SetLCCB( GetMIFParm( papszNextField, 2, 0.0 ), 
                       GetMIFParm( papszNextField, 3, 0.0 ), 
                       GetMIFParm( papszNextField, 1, 0.0 ), 
                       GetMIFParm( papszNextField, 0, 0.0 ), 
                       GetMIFParm( papszNextField, 4, 0.0 ), 
                       GetMIFParm( papszNextField, 5, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Stereographic 
         *-------------------------------------------------------------*/ 
      case 20: 
      case 31: /* double stereographic */ 
        poSR->SetStereographic(  
            GetMIFParm( papszNextField, 1, 0.0 ), 
            GetMIFParm( papszNextField, 0, 0.0 ), 
            GetMIFParm( papszNextField, 2, 1.0 ), 
            GetMIFParm( papszNextField, 3, 0.0 ), 
            GetMIFParm( papszNextField, 4, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Swiss Oblique Mercator / Cylindrical 
         *-------------------------------------------------------------*/ 
      case 25: 
        poSR->SetSOC( GetMIFParm( papszNextField, 1, 0.0 ), 
                      GetMIFParm( papszNextField, 0, 0.0 ), 
                      GetMIFParm( papszNextField, 2, 0.0 ), 
                      GetMIFParm( papszNextField, 3, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Regional Mercator 
         *-------------------------------------------------------------*/ 
      case 26: 
        poSR->SetMercator( GetMIFParm( papszNextField, 1, 0.0 ),  
                           GetMIFParm( papszNextField, 0, 0.0 ), 
                           1.0, 0.0, 0.0 ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * Polygonic 
         *-------------------------------------------------------------*/ 
      case 27: 
        poSR->SetPolyconic( GetMIFParm( papszNextField, 1, 0.0 ),  
                            GetMIFParm( papszNextField, 0, 0.0 ), 
                          GetMIFParm( papszNextField, 2, 0.0 ), 
                          GetMIFParm( papszNextField, 3, 0.0 ) ); 
        break; 
 
        /*-------------------------------------------------------------- 
         * CassiniSoldner 
         *-------------------------------------------------------------*/ 
      case 30: 
        poSR->SetCS(  
            GetMIFParm( papszNextField, 1, 0.0 ), 
            GetMIFParm( papszNextField, 0, 0.0 ), 
            GetMIFParm( papszNextField, 2, 0.0 ), 
            GetMIFParm( papszNextField, 3, 0.0 ) ); 
        break; 
 
      default: 
        break; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Set linear units.                                               */ 
/* -------------------------------------------------------------------- */ 
    if( pszUnitsName != NULL ) 
        poSR->SetLinearUnits( pszUnitsName, dfUnitsConv ); 
 
/* -------------------------------------------------------------------- */ 
/*      For Non-Earth projection, we're done at this point.             */ 
/* -------------------------------------------------------------------- */ 
    if (nProjection == 0) 
    { 
        CSLDestroy(papszFields); 
        return poSR; 
    } 
 
/* ==================================================================== */ 
/*      Establish the GeogCS                                            */ 
/* ==================================================================== */ 
    const char *pszGeogName = "unnamed"; 
    const char *pszSpheroidName = "GRS_1980"; 
    double      dfSemiMajor = 6378137.0; 
    double      dfInvFlattening = 298.257222101; 
    const char *pszPrimeM = "Greenwich"; 
    double      dfPMLongToGreenwich = 0.0; 
 
/* -------------------------------------------------------------------- */ 
/*      Find the datum, and collect it's parameters if possible.        */ 
/* -------------------------------------------------------------------- */ 
    int         iDatum; 
    MapInfoDatumInfo *psDatumInfo = NULL; 
     
    for( iDatum = 0; asDatumInfoList[iDatum].nMapInfoDatumID != -1; iDatum++ ) 
    { 
        if( asDatumInfoList[iDatum].nMapInfoDatumID == nDatum ) 
        { 
            psDatumInfo = asDatumInfoList + iDatum; 
            break; 
        } 
    } 
 
    if( asDatumInfoList[iDatum].nMapInfoDatumID == -1 
        && nDatum != 999 && nDatum != 9999 ) 
    { 
        /* use WGS84 */ 
        psDatumInfo = asDatumInfoList + 0; 
    } 
 
    if( psDatumInfo != NULL ) 
    { 
        nEllipsoid = psDatumInfo->nEllipsoid; 
        adfDatumParm[0] =  psDatumInfo->dfShiftX; 
        adfDatumParm[1] = psDatumInfo->dfShiftY; 
        adfDatumParm[2] = psDatumInfo->dfShiftZ; 
        adfDatumParm[3] = psDatumInfo->dfDatumParm0; 
        adfDatumParm[4] = psDatumInfo->dfDatumParm1; 
        adfDatumParm[5] = psDatumInfo->dfDatumParm2; 
        adfDatumParm[6] = psDatumInfo->dfDatumParm3; 
        adfDatumParm[7] = psDatumInfo->dfDatumParm4; 
    } 
     
/* -------------------------------------------------------------------- */ 
/*      Set the spheroid if it is known from the table.                 */ 
/* -------------------------------------------------------------------- */ 
    for( int i = 0; asSpheroidInfoList[i].nMapInfoId != -1; i++ ) 
    { 
        if( asSpheroidInfoList[i].nMapInfoId == nEllipsoid ) 
        { 
            dfSemiMajor = asSpheroidInfoList[i].dfA; 
            dfInvFlattening = asSpheroidInfoList[i].dfInvFlattening; 
            pszSpheroidName = asSpheroidInfoList[i].pszMapinfoName; 
            break; 
        } 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      apply datum parameters.                                         */ 
/* -------------------------------------------------------------------- */ 
    char        szDatumName[128]; 
 
    if( nDatum == 999 ) 
    { 
        sprintf( szDatumName, 
                 "MIF 9999,%d,%.15g,%.15g,%.15g", 
                 nEllipsoid, 
                 adfDatumParm[0], 
                 adfDatumParm[1], 
                 adfDatumParm[2] ); 
    } 
    else if( nDatum == 9999 ) 
    { 
        sprintf( szDatumName, 
                 "MIF 9999,%d,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g", 
                 nEllipsoid, 
                 adfDatumParm[0], 
                 adfDatumParm[1], 
                 adfDatumParm[2], 
                 adfDatumParm[3], 
                 adfDatumParm[4], 
                 adfDatumParm[5], 
                 adfDatumParm[6], 
                 adfDatumParm[7] ); 
    } 
    else if( psDatumInfo->pszOGCDatumName != NULL 
             && strlen(psDatumInfo->pszOGCDatumName) > 0 ) 
    { 
        strncpy( szDatumName, psDatumInfo->pszOGCDatumName, 
                 sizeof(szDatumName) ); 
    } 
    else 
    { 
        sprintf( szDatumName, "MIF %d", nDatum ); 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Set prime meridian for 9999 datums.                             */ 
/* -------------------------------------------------------------------- */ 
    if( nDatum == 9999 ) 
    { 
        pszPrimeM = "non-Greenwich"; 
        dfPMLongToGreenwich = adfDatumParm[7]; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Set the GeogCS.                                                 */ 
/* -------------------------------------------------------------------- */ 
    poSR->SetGeogCS( pszGeogName, szDatumName, pszSpheroidName, 
                     dfSemiMajor, dfInvFlattening, 
                     pszPrimeM, dfPMLongToGreenwich, 
                     SRS_UA_DEGREE, 
                     atof(SRS_UA_DEGREE_CONV) ); 
 
    poSR->SetTOWGS84( adfDatumParm[0], adfDatumParm[1], adfDatumParm[2], 
                      -adfDatumParm[3], -adfDatumParm[4], -adfDatumParm[5],  
                      adfDatumParm[6] ); 
 
/* -------------------------------------------------------------------- */ 
/*      Report on translation.                                          */ 
/* -------------------------------------------------------------------- */ 
    char        *pszWKT; 
 
    poSR->exportToWkt( &pszWKT ); 
    if( pszWKT != NULL ) 
    { 
        CPLDebug( "MITAB", 
                  "This CoordSys value:\n%s\nwas translated to:\n%s\n", 
                  pszCoordSys, pszWKT ); 
        CPLFree( pszWKT ); 
    } 
 
    CSLDestroy(papszFields); 
 
    return poSR; 
} 
 
/************************************************************************/ 
/*                      MITABSpatialRef2CoordSys()                      */ 
/*                                                                      */ 
/*      Converts a OGRSpatialReference object into a MIF COORDSYS       */ 
/*      string.                                                         */ 
/*                                                                      */ 
/*      The function returns a newly allocated string that should be    */ 
/*      CPLFree()'d by the caller.                                      */ 
/************************************************************************/ 
 
char *MITABSpatialRef2CoordSys( OGRSpatialReference * poSR ) 
 
{ 
    if( poSR == NULL ) 
        return NULL; 
     
    /*----------------------------------------------------------------- 
     * Get the linear units. 
     *----------------------------------------------------------------*/ 
    double      dfLinearConv; 
    char        *pszLinearUnits; 
 
    dfLinearConv = poSR->GetLinearUnits( &pszLinearUnits ); 
 
    /*----------------------------------------------------------------- 
     * Transform the projection and projection parameters. 
     *----------------------------------------------------------------*/ 
    const char *pszProjection = poSR->GetAttrValue("PROJECTION"); 
    double      parms[10]; 
    int         nProjection = 0; 
    int         nParmCount = 0; 
 
    if( pszProjection == NULL ) 
    { 
        /*-------------------------------------------------------------- 
         * NULL projection.   
         * We have 2 possibilities: CoordSys NonEarth or Lat/Lon  
         * NonEarth ... is an empty SpatialRef.   
         * Lat/Lon has no "PROJECTION" but GEOGCS is set 
         *-------------------------------------------------------------*/ 
         if ( poSR->GetAttrValue("GEOGCS") == NULL) 
            nProjection = 0; // Non-Earth 
        else 
            nProjection = 1; // Lat/Lon 
    } 
    else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) 
    { 
        nProjection = 9; 
        parms[0] = poSR->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[5] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 6; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) 
    { 
        nProjection = 5; 
        parms[0] = poSR->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); 
        parms[2] = 90.0; 
        nParmCount = 3; 
 
        if( ABS((ABS(parms[1]) - 90)) > 0.001 ) 
            nProjection = 28; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) 
    { 
        nProjection = 2; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); 
        nParmCount = 2; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_ECKERT_IV) ) 
    { 
        nProjection = 14; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_ECKERT_VI) ) 
    { 
        nProjection = 15; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) ) 
    { 
        nProjection = 6; 
        parms[0] = poSR->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[5] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 6; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_GALL_STEREOGRAPHIC) ) 
    { 
        nProjection = 17; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) 
    { 
        nProjection = 7; 
        parms[0] = poSR->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_AZIMUTH,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[5] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 6; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) 
    { 
        nProjection = 4; 
        parms[0] = poSR->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); 
        parms[2] = 90.0; 
        nParmCount = 3; 
 
        if( ABS((ABS(parms[1]) - 90)) > 0.001 ) 
            nProjection = 28; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) 
    { 
        nProjection = 3; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[5] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 6; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) 
    { 
        nProjection = 19; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[5] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 6; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) ) 
    { 
        nProjection = 10; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        nParmCount = 1; 
 
        if( parms[1] != 0.0 ) 
        { 
            nProjection = 26; 
            nParmCount = 2; 
        } 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_MILLER_CYLINDRICAL) ) 
    { 
        nProjection = 11; 
        parms[0] = poSR->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_MOLLWEIDE) ) 
    { 
        nProjection = 13; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_SWISS_OBLIQUE_CYLINDRICAL) ) 
    { 
        nProjection = 25; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 4; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_ROBINSON) ) 
    { 
        nProjection = 12; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_SINUSOIDAL) ) 
    { 
        nProjection = 16; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        nParmCount = 1; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_STEREOGRAPHIC) ) 
    { 
        nProjection = 20; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 5; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) ) 
    { 
        nProjection = 8; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[4] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 5; 
    } 
 
    // Transverse Mercator,(modified for Danish System 34 Jylland-Fyn) 
    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_21) ) 
    { 
       nProjection = 21; 
       parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
       parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
       parms[2] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
       parms[3] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
       parms[4] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
       nParmCount = 5; 
    } 
 
    // Transverse Mercator,(modified for Danish System 34 Sjaelland) 
    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_22) ) 
    { 
       nProjection = 22; 
       parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
       parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
       parms[2] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
       parms[3] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
       parms[4] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
       nParmCount = 5; 
    } 
 
    // Transverse Mercator,(modified for Danish System 34/45 Bornholm) 
    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_23) ) 
    { 
       nProjection = 23; 
       parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
       parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
       parms[2] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
       parms[3] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
       parms[4] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
       nParmCount = 5; 
    } 
 
    // Transverse Mercator,(modified for Finnish KKJ) 
    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_24) ) 
    { 
       nProjection = 24; 
       parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
       parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
       parms[2] = poSR->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); 
       parms[3] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
       parms[4] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
       nParmCount = 5; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_CASSINI_SOLDNER) ) 
    { 
        nProjection = 30; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 4; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_NEW_ZEALAND_MAP_GRID) ) 
    { 
        nProjection = 18; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 4; 
    } 
 
    else if( EQUAL(pszProjection,SRS_PT_POLYCONIC) ) 
    { 
        nProjection = 27; 
        parms[0] = poSR->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); 
        parms[1] = poSR->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); 
        parms[2] = poSR->GetProjParm(SRS_PP_FALSE_EASTING,0.0); 
        parms[3] = poSR->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); 
        nParmCount = 4; 
    } 
 
    /* ============================================================== 
     * Translate Datum and Ellipsoid 
     * ============================================================== */ 
    int         nDatum = 0; 
    double      adfDatumParm[8] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; 
    int         nEllipsoid=0; 
     
    const char *pszWKTDatum = poSR->GetAttrValue("DATUM"); 
 
    if( pszWKTDatum == NULL ) 
    { 
        nDatum = 0; 
        if( nProjection == 1 ) 
            nProjection = 0; 
    } 
     
    /*----------------------------------------------------------------- 
     * We know the MIF datum number, and need to look it up to 
     * translate into datum parameters. 
     *----------------------------------------------------------------*/ 
    else if( EQUALN(pszWKTDatum,"MIF ",4) 
             && atoi(pszWKTDatum+4) != 999 
             && atoi(pszWKTDatum+4) != 9999 ) 
    { 
        nDatum = atoi(pszWKTDatum+4); 
    } 
 
    /*----------------------------------------------------------------- 
     * We have the MIF datum parameters, and apply those directly. 
     *----------------------------------------------------------------*/ 
    else if( EQUALN(pszWKTDatum,"MIF ",4) 
             && (atoi(pszWKTDatum+4) == 999 || atoi(pszWKTDatum+4) == 9999) ) 
    { 
        char    **papszFields; 
         
        nDatum = atoi(pszWKTDatum+4); 
 
        papszFields = 
            CSLTokenizeStringComplex( pszWKTDatum+4, ",", FALSE, TRUE); 
 
        if( CSLCount(papszFields) >= 5 ) 
        { 
            nEllipsoid = atoi(papszFields[1]); 
            adfDatumParm[0] = atof(papszFields[2]); 
            adfDatumParm[1] = atof(papszFields[3]); 
            adfDatumParm[2] = atof(papszFields[4]); 
        } 
 
        if( CSLCount(papszFields) >= 10 ) 
        { 
            adfDatumParm[3] = atof(papszFields[5]); 
            adfDatumParm[4] = atof(papszFields[6]); 
            adfDatumParm[5] = atof(papszFields[7]); 
            adfDatumParm[6] = atof(papszFields[8]); 
            adfDatumParm[7] = atof(papszFields[9]); 
        } 
 
        if( CSLCount(papszFields) < 5 ) 
            nDatum = 104; /* WGS84 */ 
 
        CSLDestroy( papszFields ); 
    } 
     
    /*----------------------------------------------------------------- 
     * We have a "real" datum name.  Try to look it up and get the 
     * parameters.  If we don't find it just use WGS84. 
     *----------------------------------------------------------------*/ 
    else  
    { 
        int     i; 
 
        for( i = 0; asDatumInfoList[i].nMapInfoDatumID != -1; i++ ) 
        { 
            if( EQUAL(pszWKTDatum,asDatumInfoList[i].pszOGCDatumName) ) 
            { 
                nDatum = asDatumInfoList[i].nMapInfoDatumID; 
                break; 
            } 
        } 
 
        if( nDatum == 0 ) 
            nDatum = 104; /* WGS84 */ 
    } 
 
    /*----------------------------------------------------------------- 
     * Translate the units 
     *----------------------------------------------------------------*/ 
    const char  *pszMIFUnits = "m"; 
 
    if( nProjection == 1 ) 
        pszMIFUnits = NULL; 
    else if( pszLinearUnits == NULL ) 
        pszMIFUnits = "m"; 
    else if( dfLinearConv == 1000.0 ) 
        pszMIFUnits = "km"; 
    else if( dfLinearConv == 0.0254 || EQUAL(pszLinearUnits,"Inch") 
             || EQUAL(pszLinearUnits,"IINCH")) 
        pszMIFUnits = "in"; 
    else if( dfLinearConv == atof(SRS_UL_FOOT_CONV) 
             || EQUAL(pszLinearUnits,SRS_UL_FOOT) ) 
        pszMIFUnits = "ft"; 
    else if( EQUAL(pszLinearUnits,"YARD") || EQUAL(pszLinearUnits,"IYARD")  
             || dfLinearConv == 0.9144 ) 
        pszMIFUnits = "yd"; 
    else if( dfLinearConv == 0.001 ) 
        pszMIFUnits = "mm"; 
    else if( dfLinearConv == 0.01 ) 
        pszMIFUnits = "cm"; 
    else if( dfLinearConv == 1.0 ) 
        pszMIFUnits = "m"; 
    else if( dfLinearConv == atof(SRS_UL_US_FOOT_CONV) 
             || EQUAL(pszLinearUnits,SRS_UL_US_FOOT) ) 
        pszMIFUnits = "survey ft"; 
    else if( EQUAL(pszLinearUnits,SRS_UL_NAUTICAL_MILE) ) 
        pszMIFUnits = "nmi"; 
    else if( EQUAL(pszLinearUnits,SRS_UL_LINK)  
             || EQUAL(pszLinearUnits,"GUNTERLINK") ) 
        pszMIFUnits = "li"; 
    else if( EQUAL(pszLinearUnits,SRS_UL_CHAIN)  
             || EQUAL(pszLinearUnits,"GUNTERCHAIN") ) 
        pszMIFUnits = "ch"; 
    else if( EQUAL(pszLinearUnits,SRS_UL_ROD) ) 
        pszMIFUnits = "rd"; 
    else if( EQUAL(pszLinearUnits,"Mile")  
             || EQUAL(pszLinearUnits,"IMILE") ) 
        pszMIFUnits = "mi"; 
     
/* -------------------------------------------------------------------- */ 
/*      Build coordinate system definition.                             */ 
/* -------------------------------------------------------------------- */ 
    char        szCoordSys[256]; 
 
    if( nProjection != 0 ) 
    { 
        sprintf( szCoordSys, 
                 "Earth Projection %d", 
                 nProjection ); 
 
    } 
    else 
        sprintf( szCoordSys, 
                 "NonEarth Units" ); 
 
/* -------------------------------------------------------------------- */ 
/*      Append Datum                                                    */ 
/* -------------------------------------------------------------------- */ 
    if( nProjection != 0 ) 
    { 
        sprintf( szCoordSys + strlen(szCoordSys), 
                 ", %d", 
                 nDatum ); 
 
        if( nDatum == 999 || nDatum == 9999 ) 
        { 
            sprintf( szCoordSys + strlen(szCoordSys), 
                     ", %d, %.15g, %.15g, %.15g", 
                     nEllipsoid, 
                     adfDatumParm[0], adfDatumParm[1], adfDatumParm[2] ); 
        } 
         
        if( nDatum == 9999 ) 
        { 
            sprintf( szCoordSys + strlen(szCoordSys), 
                     ", %.15g, %.15g, %.15g, %.15g, %.15g", 
                     adfDatumParm[3], adfDatumParm[4], adfDatumParm[5], 
                     adfDatumParm[6], adfDatumParm[7] ); 
        } 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Append units.                                                   */ 
/* -------------------------------------------------------------------- */ 
    if( nProjection != 1 && pszMIFUnits != NULL ) 
    { 
        if( nProjection != 0 ) 
            strcat( szCoordSys, "," ); 
         
        sprintf( szCoordSys + strlen(szCoordSys), 
                 " \"%s\"", 
                 pszMIFUnits ); 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Append Projection Parms.                                        */ 
/* -------------------------------------------------------------------- */ 
    for( int iParm = 0; iParm < nParmCount; iParm++ ) 
        sprintf( szCoordSys + strlen(szCoordSys), 
                 ", %.15g", 
                 parms[iParm] ); 
 
/* -------------------------------------------------------------------- */ 
/*      Report on translation                                           */ 
/* -------------------------------------------------------------------- */ 
    char        *pszWKT = NULL; 
 
    poSR->exportToWkt( &pszWKT ); 
    if( pszWKT != NULL ) 
    { 
        CPLDebug( "MITAB", 
                  "This WKT Projection:\n%s\n\ntranslates to:\n%s\n", 
                  pszWKT, szCoordSys ); 
        CPLFree( pszWKT ); 
    } 
 
    return( CPLStrdup( szCoordSys ) ); 
} 
 
 
/************************************************************************/ 
/*                      MITABExtractCoordSysBounds                      */ 
/*                                                                      */ 
/* Return TRUE if MIF coordsys string contains a BOUNDS parameter and   */ 
/* Set x/y min/max values.                                              */ 
/************************************************************************/ 
 
GBool MITABExtractCoordSysBounds( const char * pszCoordSys, 
                                  double &dXMin, double &dYMin, 
                                  double &dXMax, double &dYMax ) 
 
{ 
    char        **papszFields; 
 
    if( pszCoordSys == NULL ) 
        return FALSE; 
     
    papszFields = CSLTokenizeStringComplex( pszCoordSys, " ,()", TRUE, FALSE ); 
 
    int iBounds = CSLFindString( papszFields, "Bounds" ); 
 
    if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields)) 
    { 
        dXMin = atof(papszFields[++iBounds]); 
        dYMin = atof(papszFields[++iBounds]); 
        dXMax = atof(papszFields[++iBounds]); 
        dYMax = atof(papszFields[++iBounds]); 
        CSLDestroy( papszFields ); 
        return TRUE; 
    } 
 
    CSLDestroy( papszFields ); 
    return FALSE; 
} 
 
 
/********************************************************************** 
 *                     MITABCoordSys2TABProjInfo() 
 * 
 * Convert a MIF COORDSYS string into a TABProjInfo structure. 
 * 
 * Note that it would have been possible to achieve the same by calling 
 * TABFile::SetSpatialRef( MITABCoordSys2SpatialRef() ) but this would  
 * involve lots of manipulations for cases where only a simple conversion 
 * is required. 
 * 
 * Returns 0 on success, -1 on error. 
 **********************************************************************/ 
int MITABCoordSys2TABProjInfo(const char * pszCoordSys, TABProjInfo *psProj) 
 
{ 
    char        **papszFields; 
 
    // Set all fields to zero, equivalent of NonEarth Units "mi" 
    memset(psProj, 0, sizeof(TABProjInfo)); 
 
    if( pszCoordSys == NULL ) 
        return -1; 
     
    /*----------------------------------------------------------------- 
     * Parse the passed string into words. 
     *----------------------------------------------------------------*/ 
    while(*pszCoordSys == ' ') pszCoordSys++;  // Eat leading spaces 
    if( EQUALN(pszCoordSys,"CoordSys",8) ) 
        pszCoordSys += 9; 
     
    papszFields = CSLTokenizeStringComplex( pszCoordSys, " ,", TRUE, FALSE ); 
 
    /*----------------------------------------------------------------- 
     * Clip off Bounds information. 
     *----------------------------------------------------------------*/ 
    int         iBounds = CSLFindString( papszFields, "Bounds" ); 
 
    while( iBounds != -1 && papszFields[iBounds] != NULL ) 
    { 
        CPLFree( papszFields[iBounds] ); 
        papszFields[iBounds] = NULL; 
        iBounds++; 
    } 
 
    /*----------------------------------------------------------------- 
     * Fetch the projection. 
     *----------------------------------------------------------------*/ 
    char        **papszNextField; 
 
    if( CSLCount( papszFields ) >= 3 
        && EQUAL(papszFields[0],"Earth") 
        && EQUAL(papszFields[1],"Projection") ) 
    { 
        psProj->nProjId = atoi(papszFields[2]); 
        papszNextField = papszFields + 3; 
    } 
    else if (CSLCount( papszFields ) >= 2 
             && EQUAL(papszFields[0],"NonEarth") ) 
    { 
        // NonEarth Units "..." Bounds (x, y) (x, y) 
        psProj->nProjId = 0; 
        papszNextField = papszFields + 2; 
 
        if( papszNextField[0] != NULL && EQUAL(papszNextField[0],"Units") ) 
            papszNextField++; 
    } 
    else 
    { 
        // Invalid projection string ??? 
        if (CSLCount(papszFields) > 0) 
            CPLError(CE_Warning, CPLE_IllegalArg, 
                     "Failed parsing CoordSys: '%s'", pszCoordSys); 
        CSLDestroy(papszFields); 
        return -1; 
    } 
 
    /*----------------------------------------------------------------- 
     * Fetch the datum information. 
     *----------------------------------------------------------------*/ 
    int         nDatum = 0; 
 
    if( psProj->nProjId != 0 && CSLCount(papszNextField) > 0 ) 
    { 
        nDatum = atoi(papszNextField[0]); 
        papszNextField++; 
    } 
 
    if( (nDatum == 999 || nDatum == 9999) 
        && CSLCount(papszNextField) >= 4 ) 
    { 
        psProj->nEllipsoidId = atoi(papszFields[0]); 
        psProj->dDatumShiftX = atof(papszNextField[1]); 
        psProj->dDatumShiftY = atof(papszNextField[2]); 
        psProj->dDatumShiftZ = atof(papszNextField[3]); 
        papszNextField += 4; 
 
        if( nDatum == 9999 
            && CSLCount(papszNextField) >= 5 ) 
        { 
            psProj->adDatumParams[0] = atof(papszNextField[0]); 
            psProj->adDatumParams[1] = atof(papszNextField[1]); 
            psProj->adDatumParams[2] = atof(papszNextField[2]); 
            psProj->adDatumParams[3] = atof(papszNextField[3]); 
            psProj->adDatumParams[4] = atof(papszNextField[4]); 
            papszNextField += 5; 
        } 
    } 
    else if (nDatum != 999 && nDatum != 9999) 
    { 
    /*----------------------------------------------------------------- 
     * Find the datum, and collect it's parameters if possible. 
     *----------------------------------------------------------------*/ 
        int         iDatum; 
        MapInfoDatumInfo *psDatumInfo = NULL; 
         
        for(iDatum=0; asDatumInfoList[iDatum].nMapInfoDatumID != -1; iDatum++) 
        { 
            if( asDatumInfoList[iDatum].nMapInfoDatumID == nDatum ) 
            { 
                psDatumInfo = asDatumInfoList + iDatum; 
                break; 
            } 
        } 
 
        if( asDatumInfoList[iDatum].nMapInfoDatumID == -1 
            && nDatum != 999 && nDatum != 9999 ) 
        { 
            /* use WGS84 */ 
            psDatumInfo = asDatumInfoList + 0; 
        } 
 
        if( psDatumInfo != NULL ) 
        { 
            psProj->nEllipsoidId = psDatumInfo->nEllipsoid; 
            psProj->nDatumId = psDatumInfo->nMapInfoDatumID; 
            psProj->dDatumShiftX = psDatumInfo->dfShiftX; 
            psProj->dDatumShiftY = psDatumInfo->dfShiftY; 
            psProj->dDatumShiftZ = psDatumInfo->dfShiftZ; 
            psProj->adDatumParams[0] = psDatumInfo->dfDatumParm0; 
            psProj->adDatumParams[1] = psDatumInfo->dfDatumParm1; 
            psProj->adDatumParams[2] = psDatumInfo->dfDatumParm2; 
            psProj->adDatumParams[3] = psDatumInfo->dfDatumParm3; 
            psProj->adDatumParams[4] = psDatumInfo->dfDatumParm4; 
        } 
    }     
 
    /*----------------------------------------------------------------- 
     * Fetch the units string. 
     *----------------------------------------------------------------*/ 
    if( CSLCount(papszNextField) > 0 ) 
    { 
        psProj->nUnitsId = TABUnitIdFromString(papszNextField[0]); 
        papszNextField++; 
    } 
 
    /*----------------------------------------------------------------- 
     * Finally the projection parameters. 
     *----------------------------------------------------------------*/ 
    for(int iParam=0; iParam < 6 && CSLCount(papszNextField) > 0; iParam++) 
    { 
        psProj->adProjParams[iParam] = atof(papszNextField[0]); 
        papszNextField++;          
    } 
 
    return 0; 
}