text: /******************************************************************************
   * $Id$
   *
   * Project:  libgeotiff
   * Purpose:  Code to convert a normalized GeoTIFF definition into a PROJ.4
   *           (OGDI) compatible projection string.
   * 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$
   * Revision 1.7  2010/02/11 18:10:38  alannorton
   *
   * Cleaned up code to avoid extraneous compiler warnings on windows.
   * Fixed metadata merge and vdf write to work with new DataMgr
   *
   * Revision 1.6  2009/07/14 20:34:07  alannorton
   * Changes to compile on Windows
   *
   * Revision 1.5  2009/07/13 19:03:18  clynejp
   *
   * Ported to RHEL5
   *
   * Revision 1.4  2009/05/22 22:25:54  alannorton
   * Added capability of handling lat/long projection, added option of specifying
   * corners as a command line argument.
   *
   * Revision 1.3  2009/04/06 22:12:03  alannorton
   * Modifications to geotiff to fix polar stereo and mercator projections.
   *
   * Revision 1.2  2009/04/03 20:56:13  alannorton
   * Changes to polar stereo projection parameters
   *
   * Revision 1.1  2009/04/03 18:00:05  alannorton
   * Added modified version of geotiff_proj4 because the library version did
   * not support mercator or polar stereographic projections.
   *
   * Revision 1.25  2008/05/21 04:25:01  fwarmerdam
   * avoid warnings.
   *
   * Revision 1.24  2008/05/21 04:12:57  fwarmerdam
   * added preliminary GTIFSetFromProj4() support
   *
   * Revision 1.23  2007/03/13 18:04:33  fwarmerdam
   * added new zealand map grid support per bug 1519
   *
   * Revision 1.22  2005/03/04 04:32:37  fwarmerdam
   * added cylindricalequalarea support
   *
   * Revision 1.21  2003/08/21 18:42:39  warmerda
   * fixed support for ModelTypeGeographic as per email from Young Su, Cha
   *
   * Revision 1.20  2003/07/08 17:31:30  warmerda
   * cleanup various warnings
   *
   * Revision 1.19  2002/11/29 20:57:09  warmerda
   * added LCC1SP mapping
   *
   * Revision 1.18  2002/07/09 14:47:53  warmerda
   * fixed translation of polar stereographic
   *
   * Revision 1.17  2001/11/23 19:53:56  warmerda
   * free PROJ.4 definitions after use
   *
   * Revision 1.16  2000/12/05 19:21:45  warmerda
   * added cassini support
   *
   * Revision 1.15  2000/12/05 17:44:41  warmerda
   * Use +R_A for Miller and VanDerGrinten
   *
   * Revision 1.14  2000/10/13 18:06:51  warmerda
   * added econic support for PROJ.4 translation
   *
   * Revision 1.13  2000/09/15 19:30:48  warmerda
   * *** empty log message ***
   *
   * Revision 1.12  2000/09/15 18:21:07  warmerda
   * Fixed order of parameters for LCC 2SP.  When parameters
   * were read from EPSG CSV files the standard parallels and origin
   * were mixed up.  This affects alot of state plane zones!
   *
   * Revision 1.11  2000/06/06 17:39:45  warmerda
   * Modify to work with projUV version of library.
   *
   * Revision 1.10  1999/07/06 15:05:51  warmerda
   * Fixed up LCC_1SP notes.
   *
   * Revision 1.9  1999/05/04 16:24:49  warmerda
   * Fixed projection string formating with zones.
   *
   * Revision 1.8  1999/05/04 12:27:01  geotiff
   * only emit proj unsupported warning if DEBUG defined
   *
   * Revision 1.7  1999/05/04 03:14:59  warmerda
   * fixed use of foot instead of ft for units
   *
   * Revision 1.6  1999/05/03 17:50:31  warmerda
   * avoid warnings on IRIX
   *
   * Revision 1.5  1999/04/29 23:02:24  warmerda
   * added mapsys utm test.
   *
   * Revision 1.4  1999/03/18 21:35:42  geotiff
   * Added reprojection functions
   *
   * Revision 1.3  1999/03/10 18:11:17  geotiff
   * Removed comment about this not being the master ... now it is.
   *
   * Revision 1.2  1999/03/10 18:10:27  geotiff
   * Avoid use of cpl_serv.h and CPLStrdup().
   *
   * Revision 1.1  1999/03/10 15:20:43  geotiff
   * New
   *
   */
  
  #include "cpl_serv.h"
  #include "geotiff.h"
  #include "geo_normalize.h"
  #include "geovalues.h"
  #ifdef WIN32
  #pragma warning( disable : 4996 )
  #endif
  
  /************************************************************************/
  /*                          OSRProj4Tokenize()                          */
  /*                                                                      */
  /*      Custom tokenizing function for PROJ.4 strings.  The main        */
  /*      reason we can't just use CSLTokenizeString is to handle         */
  /*      strings with a + sign in the exponents of parameter values.     */
  /************************************************************************/
  
  static char **OSRProj4Tokenize( const char *pszFull )
  
  {
      char *pszStart = NULL;
      char *pszFullWrk;
      char **papszTokens = (char **) calloc(sizeof(char*),200);
      int  i;
      int  nTokens = 0;
  
      if( pszFull == NULL )
          return NULL;
  
      pszFullWrk = strdup( pszFull );
  
      for( i=0; pszFullWrk[i] != '\0'; i++ )
      {
          switch( pszFullWrk[i] )
          {
            case '+':
              if( i == 0 || pszFullWrk[i-1] == '\0' )
              {
                  if( pszStart != NULL )
                  {
                      if( strstr(pszStart,"=") != NULL )
                      {
                          papszTokens[nTokens++] = strdup(pszStart);
                      }
                      else
                      {
                          char szAsBoolean[100];
                          strcpy( szAsBoolean,pszStart);
                          strcat( szAsBoolean,"=yes" );
                          papszTokens[nTokens++] = strdup(szAsBoolean);
                      }
                  }
                  pszStart = pszFullWrk + i + 1;
              }
              break;
  
            case ' ':
            case '\t':
            case '\n':
              pszFullWrk[i] = '\0';
              break;
  
            default:
              break;
          }
      }
  
      if( pszStart != NULL && strlen(pszStart) > 0 )
      {
          papszTokens[nTokens++] = strdup( pszStart );
      }
  
      free( pszFullWrk );
  
      return papszTokens;
  }
  
  
  /************************************************************************/
  /*                              OSR_GSV()                               */
  /************************************************************************/
  
  static const char *OSR_GSV( char **papszNV, const char * pszField )
  
  {
      int field_len = (int)strlen(pszField);
      int i;
      
      if( !papszNV )
          return NULL;
  
      for( i = 0; papszNV[i] != NULL; i++ )
      {
          if( EQUALN(papszNV[i],pszField,field_len) )
          {
              if( papszNV[i][field_len] == '=' )
                  return papszNV[i] + field_len + 1;
  
              if( strlen(papszNV[i]) == field_len )
                  return "";
          }
      }
  
      return NULL;
  }
  
  /************************************************************************/
  /*                              OSR_GDV()                               */
  /*                                                                      */
  /*      Fetch a particular parameter out of the parameter list, or      */
  /*      the indicated default if it isn't available.  This is a         */
  /*      helper function for importFromProj4().                          */
  /************************************************************************/
  
  static double OSR_GDV( char **papszNV, const char * pszField, 
                         double dfDefaultValue )
  
  {
      const char *pszValue = OSR_GSV( papszNV, pszField );
  
      // special hack to use k_0 if available.
      if( pszValue == NULL && EQUAL(pszField,"k") )
          return OSR_GDV( papszNV, "k_0", dfDefaultValue );
  
      if( pszValue == NULL )
          return dfDefaultValue;
      else
          return atof(pszValue);
  }
  
  /************************************************************************/
  /*                         OSRFreeStringList()                          */
  /************************************************************************/
  
  static void OSRFreeStringList( char ** list )
  
  {
      int i;
  
      for( i = 0; list != NULL && list[i] != NULL; i++ )
          free( list[i] );
      free(list);
  }
  
  
  /************************************************************************/
  /*                          GTIFSetFromProj4_WRF()                      */
  /* version modified from geotiff library version of GTIFSetFromPRoj4    */
  /* to handle mercator and polar stereo projections as required in WRF   */
  /*    Modifications by Alan Norton, April 2009                          */
  /************************************************************************/
  
  int GTIFSetFromProj4_WRF( GTIF *gtif, const char *proj4 )
  
  {
      char **papszNV = OSRProj4Tokenize( proj4 );
      short nSpheroid = KvUserDefined;
      double dfSemiMajor=0.0, dfSemiMinor=0.0, dfInvFlattening=0.0;
      int	   nDatum = KvUserDefined;
      int    nGCS = KvUserDefined;    
      const char  *value;
  
  /* -------------------------------------------------------------------- */
  /*      Get the ellipsoid definition.                                   */
  /* -------------------------------------------------------------------- */
      value = OSR_GSV( papszNV, "ellps" );
  
      if( value == NULL )
      {
          nSpheroid = Ellipse_Sphere;
      }
      else if( EQUAL(value,"WGS84") )
          nSpheroid = Ellipse_WGS_84;
      else if( EQUAL(value,"clrk66") )
          nSpheroid = Ellipse_Clarke_1866;
      else if( EQUAL(value,"clrk80") )
          nSpheroid = Ellipse_Clarke_1880;
      else if( EQUAL(value,"GRS80") )
          nSpheroid = Ellipse_GRS_1980;
  
      if( nSpheroid == KvUserDefined )
      {
          dfSemiMajor = OSR_GDV(papszNV,"a",0.0);
          dfSemiMinor = OSR_GDV(papszNV,"b",0.0);
          dfInvFlattening = OSR_GDV(papszNV,"rf",0.0);
          if( dfSemiMinor != 0.0 && dfInvFlattening == 0.0 )
              dfInvFlattening = -1.0 / (dfSemiMinor/dfSemiMajor - 1.0);
      }
      
  /* -------------------------------------------------------------------- */
  /*      Get the GCS/Datum code.                                         */
  /* -------------------------------------------------------------------- */
      value = OSR_GSV( papszNV, "datum" );
  
      if( value == NULL ) 
      {
      }
      else if( EQUAL(value,"WGS84") )
      {
          nGCS = GCS_WGS_84;
          nDatum = Datum_WGS84;
      }
      else if( EQUAL(value,"NAD83") )
      {
          nGCS = GCS_NAD83;
          nDatum = Datum_North_American_Datum_1983;
      }
      else if( EQUAL(value,"NAD27") )
      {
          nGCS = GCS_NAD27;
          nDatum = Datum_North_American_Datum_1927;
      }
  
  /* -------------------------------------------------------------------- */
  /*      Operate on the basis of the projection name.                    */
  /* -------------------------------------------------------------------- */
      value = OSR_GSV(papszNV,"proj");
  
      if( value == NULL )
      {
          OSRFreeStringList( papszNV );
          return FALSE;
      }
  
      else if( EQUAL(value,"longlat") || EQUAL(value,"latlong") )
      {
  		
  		GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeGeographic);
  		GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1,
                     RasterPixelIsArea);
  		GTIFKeySet(gtif, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
                     Angular_Degree);
  				   
      }
  	/* Handle just the WRF-required mercator tags:*/
  	else if( EQUAL(value, "merc"))
  	{
  		
  		GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  		GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  		GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  		GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
  		   CT_Mercator );
  		GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_ts", 0.0 ) );
  		GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lon_0", 0.0 ) );
  		GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "k", 1.0 ) );
  		GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "x_0", 0.0 ) );
          GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
  
  		//specify lat_ts as standard parallel 1 (default 0? ????)
  		GTIFKeySet(gtif, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_0", 0.0 ) );
          
  	}
      else if( EQUAL(value,"tmerc") )
      {
  		GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  		GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  		GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  
  		GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
  		   CT_TransverseMercator );
  
          GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_0", 0.0 ) );
  
          GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lon_0", 0.0 ) );
          
          GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "k", 1.0 ) );
          
          GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "x_0", 0.0 ) );
          
          GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"utm") )
      {
          int nZone = (int) OSR_GDV(papszNV,"zone",0);
          const char *south = OSR_GSV(papszNV,"south");
                           
  	GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  	GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  	GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  
  	GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
  		   CT_TransverseMercator );
  
          GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, 
                     0.0 );
  
          GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
                     nZone * 6 - 183.0 );
          
          GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
                     0.9996 );
          
          GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
                     500000.0 );
  
          if( south != NULL )
              GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                         10000000.0 );
          else
              GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                         0.0 );
      }
  	/* For polar stereo only handle pure north or south projections*/
  	else if (EQUAL(value,"stere") )
  	{
  		
  		GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  		GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  		GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  		GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
  		   CT_PolarStereographic );
  		
  
  		//specify lat_ts as natural origin latitude
  		GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_ts", 90.0 ) );
  		GTIFKeySet(gtif, ProjStraightVertPoleLongGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lon_0", 0.0 ) );
  		GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "k", 1.0 ) );
  		GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "x_0", 0.0 ) );
          GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
          
  	}
      else if( EQUAL(value,"lcc") 
               && OSR_GDV(papszNV, "lat_0", 0.0 ) 
               == OSR_GDV(papszNV, "lat_1", 0.0 ) )
      {
  	GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  	GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  	GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  
  	GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
  		   CT_LambertConfConic_1SP );
  
          GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_0", 0.0 ) );
  
          GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lon_0", 0.0 ) );
          
          GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "k", 1.0 ) );
          
          GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "x_0", 0.0 ) );
          
          GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"lcc") 
               && OSR_GDV(papszNV, "lat_0", 0.0 ) 
               != OSR_GDV(papszNV, "lat_1", 0.0 ) )
      {
  	GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  	GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  	GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  
  	GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
  		   CT_LambertConfConic_2SP );
  
          GTIFKeySet(gtif, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_0", 0.0 ) );
  
          GTIFKeySet(gtif, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lon_0", 0.0 ) );
          
          GTIFKeySet(gtif, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_1", 0.0 ) );
          
          GTIFKeySet(gtif, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_2", 0.0 ) );
          
          GTIFKeySet(gtif, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "x_0", 0.0 ) );
          
          GTIFKeySet(gtif, ProjFalseOriginNorthingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
      else if( EQUAL(value,"eqc") )
      {
  		GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1,
                     ModelTypeProjected);
  
          GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  
          GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1,
                     KvUserDefined );
  
          GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1,
                     CT_Equirectangular );
  
          GTIFKeySet(gtif, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_0", 0.0 ) );
  
          GTIFKeySet(gtif, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lon_0", 0.0 ) );
  
          if( OSR_GDV( papszNV, "lat_0", 0.0 ) != OSR_GDV( papszNV, "lat_ts", 0.0 ) )	{
  			GTIFKeySet(gtif, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "lat_ts", 0.0 ) );
  	}
          GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "x_0", 0.0 ) );
  
          GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
  
      }
  
  #ifdef notdef
      else if( EQUAL(value,"bonne") )
      {
          SetBonne( OSR_GDV( papszNV, "lat_1", 0.0 ), 
                    OSR_GDV( papszNV, "lon_0", 0.0 ), 
                    OSR_GDV( papszNV, "x_0", 0.0 ), 
                    OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"cass") )
      {
          SetCS( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"nzmg") )
      {
          SetNZMG( OSR_GDV( papszNV, "lat_0", -41.0 ), 
                   OSR_GDV( papszNV, "lon_0", 173.0 ), 
                   OSR_GDV( papszNV, "x_0", 2510000.0 ), 
                   OSR_GDV( papszNV, "y_0", 6023150.0 ) );
      }
  
      else if( EQUAL(value,"cea") )
      {
          SetCEA( OSR_GDV( papszNV, "lat_ts", 0.0 ), 
                  OSR_GDV( papszNV, "lon_0", 0.0 ), 
                  OSR_GDV( papszNV, "x_0", 0.0 ), 
                  OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"merc") /* 2SP form */
               && OSR_GDV(papszNV, "lat_ts", 1000.0) < 999.0 )
      {
          SetMercator2SP( OSR_GDV( papszNV, "lat_ts", 0.0 ), 
                          0.0,
                          OSR_GDV( papszNV, "lon_0", 0.0 ), 
                          OSR_GDV( papszNV, "x_0", 0.0 ), 
                          OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"merc") ) /* 1SP form */
      {
          SetMercator( 0.0,
                       OSR_GDV( papszNV, "lon_0", 0.0 ), 
                       OSR_GDV( papszNV, "k", 1.0 ), 
                       OSR_GDV( papszNV, "x_0", 0.0 ), 
                       OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"stere") 
               && ABS(OSR_GDV( papszNV, "lat_0", 0.0 ) - 90) < 0.001 )
      {
          SetPS( OSR_GDV( papszNV, "lat_ts", 90.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "k", 1.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"stere") 
               && ABS(OSR_GDV( papszNV, "lat_0", 0.0 ) + 90) < 0.001 )
      {
          SetPS( OSR_GDV( papszNV, "lat_ts", -90.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "k", 1.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUALN(value,"stere",5) /* mostly sterea */
               && CSLFetchNameValue(papszNV,"k") != NULL )
      {
          SetOS( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "k", 1.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"stere") )
      {
          SetStereographic( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                            OSR_GDV( papszNV, "lon_0", 0.0 ), 
                            1.0, 
                            OSR_GDV( papszNV, "x_0", 0.0 ), 
                            OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"eqc") )
      {
          if( OSR_GDV( papszNV, "lat_0", 0.0 ) != OSR_GDV( papszNV, "lat_ts", 0.0 ) )
            SetEquirectangular2( OSR_GDV( papszNV, "lat_0", 0.0 ),
                                 OSR_GDV( papszNV, "lon_0", 0.0 )+dfFromGreenwich,
                                 OSR_GDV( papszNV, "lat_ts", 0.0 ),
                                 OSR_GDV( papszNV, "x_0", 0.0 ),
                                 OSR_GDV( papszNV, "y_0", 0.0 ) );
          else
            SetEquirectangular( OSR_GDV( papszNV, "lat_ts", 0.0 ),
                                OSR_GDV( papszNV, "lon_0", 0.0 )+dfFromGreenwich,
                                OSR_GDV( papszNV, "x_0", 0.0 ),
                                OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
     else if( EQUAL(value,"glabsgm") )
     {
         SetGaussLabordeReunion( OSR_GDV( papszNV, "lat_0", -21.116666667 ),
                                 OSR_GDV( papszNV, "lon_0", 55.53333333309)+dfFromGreenwich,
                                 OSR_GDV( papszNV, "k_0", 1.0 ),
                                 OSR_GDV( papszNV, "x_0", 160000.000 ),
                                 OSR_GDV( papszNV, "y_0", 50000.000 ) );
     }
  
      else if( EQUAL(value,"gnom") )
      {
          SetGnomonic( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                       OSR_GDV( papszNV, "lon_0", 0.0 ), 
                       OSR_GDV( papszNV, "x_0", 0.0 ), 
                       OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"ortho") )
      {
          SetOrthographic( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                           OSR_GDV( papszNV, "lon_0", 0.0 ), 
                           OSR_GDV( papszNV, "x_0", 0.0 ), 
                           OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"laea") )
      {
          SetLAEA( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                   OSR_GDV( papszNV, "lon_0", 0.0 ), 
                   OSR_GDV( papszNV, "x_0", 0.0 ), 
                   OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"aeqd") )
      {
          SetAE( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"eqdc") )
      {
          SetEC( OSR_GDV( papszNV, "lat_1", 0.0 ), 
                 OSR_GDV( papszNV, "lat_2", 0.0 ), 
                 OSR_GDV( papszNV, "lat_0", 0.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"mill") )
      {
          SetMC( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                 OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"moll") )
      {
          SetMollweide( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                        OSR_GDV( papszNV, "x_0", 0.0 ), 
                        OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"eck4") )
      {
          SetEckertIV( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                       OSR_GDV( papszNV, "x_0", 0.0 ), 
                       OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"eck6") )
      {
          SetEckertVI( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                       OSR_GDV( papszNV, "x_0", 0.0 ), 
                       OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"poly") )
      {
          SetPolyconic( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                        OSR_GDV( papszNV, "lon_0", 0.0 ), 
                        OSR_GDV( papszNV, "x_0", 0.0 ), 
                        OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"aea") )
      {
          SetACEA( OSR_GDV( papszNV, "lat_1", 0.0 ), 
                   OSR_GDV( papszNV, "lat_2", 0.0 ), 
                   OSR_GDV( papszNV, "lat_0", 0.0 ), 
                   OSR_GDV( papszNV, "lon_0", 0.0 ), 
                   OSR_GDV( papszNV, "x_0", 0.0 ), 
                   OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"robin") )
      {
          SetRobinson( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                       OSR_GDV( papszNV, "x_0", 0.0 ), 
                       OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"vandg") )
      {
          SetVDG( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                  OSR_GDV( papszNV, "x_0", 0.0 ), 
                  OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"sinu") )
      {
          SetSinusoidal( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                         OSR_GDV( papszNV, "x_0", 0.0 ), 
                         OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"gall") )
      {
          SetGS( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"goode") )
      {
          SetGH( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                 OSR_GDV( papszNV, "x_0", 0.0 ), 
                 OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"geos") )
      {
          SetGEOS( OSR_GDV( papszNV, "lon_0", 0.0 ), 
                   OSR_GDV( papszNV, "h", 35785831.0 ), 
                   OSR_GDV( papszNV, "x_0", 0.0 ), 
                   OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"lcc") ) 
      {
          if( OSR_GDV(papszNV, "lat_0", 0.0 ) 
              == OSR_GDV(papszNV, "lat_1", 0.0 ) )
          {
              /* 1SP form */
              SetLCC1SP( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                         OSR_GDV( papszNV, "lon_0", 0.0 ), 
                         OSR_GDV( papszNV, "k_0", 1.0 ), 
                         OSR_GDV( papszNV, "x_0", 0.0 ), 
                         OSR_GDV( papszNV, "y_0", 0.0 ) );
          }
          else
          {
              /* 2SP form */
              SetLCC( OSR_GDV( papszNV, "lat_1", 0.0 ), 
                      OSR_GDV( papszNV, "lat_2", 0.0 ), 
                      OSR_GDV( papszNV, "lat_0", 0.0 ), 
                      OSR_GDV( papszNV, "lon_0", 0.0 ), 
                      OSR_GDV( papszNV, "x_0", 0.0 ), 
                      OSR_GDV( papszNV, "y_0", 0.0 ) );
          }
      }
  
      else if( EQUAL(value,"omerc") )
      {
          SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                  OSR_GDV( papszNV, "lonc", 0.0 ), 
                  OSR_GDV( papszNV, "alpha", 0.0 ), 
                  0.0, /* ??? */
                  OSR_GDV( papszNV, "k", 1.0 ), 
                  OSR_GDV( papszNV, "x_0", 0.0 ), 
                  OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"somerc") )
      {
          SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                  OSR_GDV( papszNV, "lon_0", 0.0 ), 
                  90.0,  90.0, 
                  OSR_GDV( papszNV, "k", 1.0 ), 
                  OSR_GDV( papszNV, "x_0", 0.0 ), 
                  OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"krovak") )
      {
          SetKrovak( OSR_GDV( papszNV, "lat_0", 0.0 ), 
                     OSR_GDV( papszNV, "lon_0", 0.0 ), 
                     OSR_GDV( papszNV, "alpha", 0.0 ), 
                     0.0, // pseudo_standard_parallel_1
                     OSR_GDV( papszNV, "k", 1.0 ), 
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "iwm_p") )
      {
          SetIWMPolyconic( OSR_GDV( papszNV, "lat_1", 0.0 ), 
                           OSR_GDV( papszNV, "lat_2", 0.0 ),
                           OSR_GDV( papszNV, "lon_0", 0.0 ), 
                           OSR_GDV( papszNV, "x_0", 0.0 ), 
                           OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag1") )
      {
          SetWagner( 1, 0.0,
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag2") )
      {
          SetWagner( 2, 0.0,
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag3") )
      {
          SetWagner( 3,
                     OSR_GDV( papszNV, "lat_ts", 0.0 ),
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag1") )
      {
          SetWagner( 4, 0.0,
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag1") )
      {
          SetWagner( 5, 0.0,
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag1") )
      {
          SetWagner( 6, 0.0,
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value, "wag1") )
      {
          SetWagner( 7, 0.0,
                     OSR_GDV( papszNV, "x_0", 0.0 ), 
                     OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  
      else if( EQUAL(value,"tpeqd") )
      {
          SetTPED( OSR_GDV( papszNV, "lat_1", 0.0 ), 
                   OSR_GDV( papszNV, "lon_1", 0.0 ), 
                   OSR_GDV( papszNV, "lat_2", 0.0 ), 
                   OSR_GDV( papszNV, "lon_2", 0.0 ), 
                   OSR_GDV( papszNV, "x_0", 0.0 ), 
                   OSR_GDV( papszNV, "y_0", 0.0 ) );
      }
  #endif
      else
      {
          /* unsupported coordinate system */
          OSRFreeStringList( papszNV );
          return FALSE;
      }
  
  /* -------------------------------------------------------------------- */
  /*      Write the GCS if we have it, otherwise write the datum.         */
  /* -------------------------------------------------------------------- */
      if( nGCS != KvUserDefined )
      {
          GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT,
                      1, nGCS );
      }
      else
      {
          GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT, 1, 
                      KvUserDefined );
          GTIFKeySet( gtif, GeogGeodeticDatumGeoKey, TYPE_SHORT,
                      1, nDatum );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Write the ellipsoid if we don't know the GCS.                   */
  /* -------------------------------------------------------------------- */
      if( nGCS == KvUserDefined )
      {
          if( nSpheroid != KvUserDefined )
              GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1, 
                          nSpheroid );
          else
          {
              GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1, 
                          KvUserDefined );
              GTIFKeySet( gtif, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
                          dfSemiMajor );
              if( dfInvFlattening == 0.0 )
                  GTIFKeySet( gtif, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1,
                              dfSemiMajor );
              else
                  GTIFKeySet( gtif, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
                              dfInvFlattening );
          }
          
      }
  
  /* -------------------------------------------------------------------- */
  /*      Linear units translation                                        */
  /* -------------------------------------------------------------------- */
      value = OSR_GSV( papszNV, "units" );
  
      if( value == NULL )
      {
          value = OSR_GSV( papszNV, "to_meter" );
          if( value )
          {
              GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, 
                          KvUserDefined );
              GTIFKeySet( gtif, ProjLinearUnitSizeGeoKey, TYPE_DOUBLE, 1, 
                          atof(value) );
          }
      }
      else if( EQUAL(value,"meter") || EQUAL(value,"m") )
      {
          GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, 
                      Linear_Meter );
      } 
      else if( EQUAL(value,"us-ft") )
      {
          GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, 
                      Linear_Foot_US_Survey );
      } 
      else if( EQUAL(value,"ft") )
      {
          GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, 
                      Linear_Foot );
      } 
  
  
      OSRFreeStringList( papszNV );
      
      return TRUE;
  }
  
  /************************************************************************/
  /*                          GTIFGetProj4Defn()                          */
  /************************************************************************/
  
  char * GTIFGetProj4Defn( GTIFDefn * psDefn )
  
  {
      char	szProjection[512];
      char	szUnits[24];
      double      dfFalseEasting, dfFalseNorthing;
  
      szProjection[0] = '\0';
      
  /* ==================================================================== */
  /*      Translate the units of measure.                                 */
  /*                                                                      */
  /*      Note that even with a +units, or +to_meter in effect, it is     */
  /*      still assumed that all the projection parameters are in         */
  /*      meters.                                                         */
  /* ==================================================================== */
      if( psDefn->UOMLength == Linear_Meter )
      {
          strcpy( szUnits, "+units=m " ); 
      }
      else if( psDefn->UOMLength == Linear_Foot )
      {
          strcpy( szUnits, "+units=ft " );
      }
      else if( psDefn->UOMLength == Linear_Foot_US_Survey )
      {
          strcpy( szUnits, "+units=us-ft " );
      }
      else if( psDefn->UOMLength == Linear_Foot_Indian )
      {
          strcpy( szUnits, "+units=ind-ft " );
      }
      else if( psDefn->UOMLength == Linear_Link )
      {
          strcpy( szUnits, "+units=link " );
      }
      else if( psDefn->UOMLength == Linear_Yard_Indian)
      {
          strcpy( szUnits, "+units=ind-yd " );
      }
      else if( psDefn->UOMLength == Linear_Fathom )
      {
          strcpy( szUnits, "+units=fath " );
      }
      else if( psDefn->UOMLength == Linear_Mile_International_Nautical )
      {
          strcpy( szUnits, "+units=kmi " );
      }
      else
      {
          sprintf( szUnits, "+to_meter=%.10f", psDefn->UOMLengthInMeters );
      }
  
  /* -------------------------------------------------------------------- */
  /*      false easting and northing are in meters and that is what       */
  /*      PROJ.4 wants regardless of the linear units.                    */
  /* -------------------------------------------------------------------- */
      dfFalseEasting = psDefn->ProjParm[5];
      dfFalseNorthing = psDefn->ProjParm[6];
      
  /* ==================================================================== */
  /*      Handle general projection methods.                              */
  /* ==================================================================== */
   
  /* -------------------------------------------------------------------- */
  /*      Geographic.                                                     */
  /* -------------------------------------------------------------------- */
      if(psDefn->Model==ModelTypeGeographic)
      {
          sprintf(szProjection+strlen(szProjection),"+proj=latlong ");
          
      }
   
  /* -------------------------------------------------------------------- */
  /*      UTM - special case override on transverse mercator so things    */
  /*      will be more meaningful to the user.                            */
  /* -------------------------------------------------------------------- */
      else if( psDefn->MapSys == MapSys_UTM_North )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=utm +zone=%d ",
                   psDefn->Zone );
      }
      
  /* -------------------------------------------------------------------- */
  /*      Transverse Mercator                                             */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_TransverseMercator )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=tmerc +lat_0=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[4],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Mercator							*/
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Mercator )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=merc +lat_ts=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[4],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Cassini/Soldner                                                 */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_CassiniSoldner )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=cass +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Oblique Stereographic - Should this really map onto             */
  /*      Stereographic?                                                  */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_ObliqueStereographic )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=stere +lat_0=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[4],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Stereographic                                                   */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Stereographic )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=stere +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Polar Stereographic                                             */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_PolarStereographic )
      {
          if( psDefn->ProjParm[0] > 0.0 )
              sprintf( szProjection+strlen(szProjection),
                       "+proj=stere +lat_0=90 +lat_ts=%.9f +lon_0=%.9f "
                       "+k=%.9f +x_0=%.3f +y_0=%.3f ",
                       psDefn->ProjParm[0],
                       psDefn->ProjParm[1],
                       psDefn->ProjParm[4],
                       dfFalseEasting,
                       dfFalseNorthing );
          else
              sprintf( szProjection+strlen(szProjection),
                       "+proj=stere +lat_0=-90 +lat_ts=%.9f +lon_0=%.9f "
                       "+k=%.9f +x_0=%.3f +y_0=%.3f ",
                       psDefn->ProjParm[0],
                       psDefn->ProjParm[1],
                       psDefn->ProjParm[4],
                       dfFalseEasting,
                       dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Equirectangular                                                 */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Equirectangular )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=eqc +lat_ts=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Gnomonic                                                        */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Gnomonic )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=gnom +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Orthographic                                                    */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Orthographic )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=ortho +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Lambert Azimuthal Equal Area                                    */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_LambertAzimEqualArea )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=laea +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Azimuthal Equidistant                                           */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_AzimuthalEquidistant )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=aeqd +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Miller Cylindrical                                              */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_MillerCylindrical )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=mill +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f +R_A ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      Polyconic                                                       */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Polyconic )
      {
          sprintf( szProjection+strlen(szProjection),
             "+proj=poly +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
  
  /* -------------------------------------------------------------------- */
  /*      AlbersEqualArea                                                 */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_AlbersEqualArea )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=aea +lat_1=%.9f +lat_2=%.9f +lat_0=%.9f +lon_0=%.9f"
                   " +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[2],
                   psDefn->ProjParm[3],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
      
  /* -------------------------------------------------------------------- */
  /*      EquidistantConic                                                */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_EquidistantConic )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=eqdc +lat_1=%.9f +lat_2=%.9f +lat_0=%.9f +lon_0=%.9f"
                   " +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[2],
                   psDefn->ProjParm[3],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
      
  /* -------------------------------------------------------------------- */
  /*      Robinson                                                        */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Robinson )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=robin +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
      
  /* -------------------------------------------------------------------- */
  /*      VanDerGrinten                                                   */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_VanDerGrinten )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=vandg +lon_0=%.9f +x_0=%.3f +y_0=%.3f +R_A ",
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
      
  /* -------------------------------------------------------------------- */
  /*      Sinusoidal                                                      */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_Sinusoidal )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=sinu +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[1],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
      
  /* -------------------------------------------------------------------- */
  /*      LambertConfConic_2SP                                            */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_LambertConfConic_2SP )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=lcc +lat_0=%.9f +lon_0=%.9f +lat_1=%.9f +lat_2=%.9f "
                   " +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[2],
                   psDefn->ProjParm[3],
                   dfFalseEasting,
                   dfFalseNorthing );
      }
      
  /* -------------------------------------------------------------------- */
  /*      LambertConfConic_1SP                                            */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_LambertConfConic_1SP )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=lcc +lat_0=%.9f +lat_1=%.9f +lon_0=%.9f"
                   " +k_0=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[4],
                   psDefn->ProjParm[5],
                   psDefn->ProjParm[6] );
      }
      
  /* -------------------------------------------------------------------- */
  /*      CT_CylindricalEqualArea                                         */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_CylindricalEqualArea )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=cea +lat_ts=%.9f +lon_0=%.9f "
                   " +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[5],
                   psDefn->ProjParm[6] );
      }
      
  /* -------------------------------------------------------------------- */
  /*      NewZealandMapGrid                                               */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_NewZealandMapGrid )
      {
          sprintf( szProjection+strlen(szProjection),
                   "+proj=nzmg +lat_0=%.9f +lon_0=%.9f"
                   " +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[5],
                   psDefn->ProjParm[6] );
      }
      
  /* -------------------------------------------------------------------- */
  /*      Transverse Mercator - south oriented.                           */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_TransvMercator_SouthOriented )
      {
          /* this appears to be an unsupported formulation with PROJ.4 */
      }
      
  /* -------------------------------------------------------------------- */
  /*      ObliqueMercator (Hotine)                                        */
  /* -------------------------------------------------------------------- */
      else if( psDefn->CTProjection == CT_ObliqueMercator )
      {
          /* not clear how ProjParm[3] - angle from rectified to skewed grid -
             should be applied ... see the +not_rot flag for PROJ.4.
             Just ignoring for now. */
  
          sprintf( szProjection+strlen(szProjection),
                   "+proj=omerc +lat_0=%.9f +lonc=%.9f +alpha=%.9f"
                   " +k=%.9f +x_0=%.3f +y_0=%.3f ",
                   psDefn->ProjParm[0],
                   psDefn->ProjParm[1],
                   psDefn->ProjParm[2],
                   psDefn->ProjParm[4],
                   psDefn->ProjParm[5],
                   psDefn->ProjParm[6] );
      }
  
  /* ==================================================================== */
  /*      Handle ellipsoid information.                                   */
  /* ==================================================================== */
      if( psDefn->Ellipsoid == Ellipse_WGS_84 )
          strcat( szProjection, "+ellps=WGS84 " );
      else if( psDefn->Ellipsoid == Ellipse_Clarke_1866 )
          strcat( szProjection, "+ellps=clrk66 " );
      else if( psDefn->Ellipsoid == Ellipse_Clarke_1880 )
          strcat( szProjection, "+ellps=clrk80 " );
      else if( psDefn->Ellipsoid == Ellipse_GRS_1980 )
          strcat( szProjection, "+ellps=GRS80 " );
      else
      {
          if( psDefn->SemiMajor != 0.0 && psDefn->SemiMinor != 0.0 )
          {
              sprintf( szProjection+strlen(szProjection),
                       "+a=%.3f +b=%.3f ",
                       psDefn->SemiMajor,
                       psDefn->SemiMinor );
          }
      }
  
      strcat( szProjection, szUnits );
  
      return( strdup( szProjection ) );
  }
  
  #if !defined(HAVE_LIBPROJ) || !defined(HAVE_PROJECTS_H)
  
  int GTIFProj4ToLatLong( GTIFDefn * psDefn, int nPoints,
                          double *padfX, double *padfY )
  {
      (void) psDefn;
      (void) nPoints;
      (void) padfX;
      (void) padfY;
  #ifdef DEBUG    
      fprintf( stderr,
               "GTIFProj4ToLatLong() - PROJ.4 support not compiled in.\n" );
  #endif    
      return FALSE;
  }
  
  int GTIFProj4FromLatLong( GTIFDefn * psDefn, int nPoints,
                            double *padfX, double *padfY )
  {
      (void) psDefn;
      (void) nPoints;
      (void) padfX;
      (void) padfY;
  #ifdef DEBUG    
      fprintf( stderr,
               "GTIFProj4FromLatLong() - PROJ.4 support not compiled in.\n" );
  #endif    
      return FALSE;
  }
  #else
  
  #include "proj_api.h"
  
  #ifdef USE_PROJUV
  #  define UV projUV
  #endif
  
  /************************************************************************/
  /*                        GTIFProj4FromLatLong()                        */
  /*                                                                      */
  /*      Convert lat/long values to projected coordinate for a           */
  /*      particular definition.                                          */
  /************************************************************************/
  
  int GTIFProj4FromLatLong( GTIFDefn * psDefn, int nPoints,
                            double *padfX, double *padfY )
  
  {
      char	*pszProjection, **papszArgs;
      projPJ		psPJ;
      int		i;
      
  /* -------------------------------------------------------------------- */
  /*      Get a projection definition.                                    */
  /* -------------------------------------------------------------------- */
      pszProjection = GTIFGetProj4Defn( psDefn );
  
      if( pszProjection == NULL )
          return FALSE;
  
  /* -------------------------------------------------------------------- */
  /*      Parse into tokens for pj_init(), and initialize the projection. */
  /* -------------------------------------------------------------------- */
      
      papszArgs = CSLTokenizeStringComplex( pszProjection, " +", TRUE, FALSE );
      free( pszProjection );
  
      psPJ = pj_init( CSLCount(papszArgs), papszArgs );
  
      CSLDestroy( papszArgs );
  
      if( psPJ == NULL )
      {
          return FALSE;
      }
  
  /* -------------------------------------------------------------------- */
  /*      Process each of the points.                                     */
  /* -------------------------------------------------------------------- */
      for( i = 0; i < nPoints; i++ )
      {
          projUV	sUV;
  
          sUV.u = padfX[i] * DEG_TO_RAD;
          sUV.v = padfY[i] * DEG_TO_RAD;
  
          sUV = pj_fwd( sUV, psPJ );
  
          padfX[i] = sUV.u;
          padfY[i] = sUV.v;
      }
  
      pj_free( psPJ );
  
      return TRUE;
  }
  
  /************************************************************************/
  /*                         GTIFProj4ToLatLong()                         */
  /*                                                                      */
  /*      Convert projection coordinates to lat/long for a particular     */
  /*      definition.                                                     */
  /************************************************************************/
  
  int GTIFProj4ToLatLong( GTIFDefn * psDefn, int nPoints,
                          double *padfX, double *padfY )
  
  {
      char	*pszProjection, **papszArgs;
      projPJ		psPJ;
      int		i;
      
  /* -------------------------------------------------------------------- */
  /*      Get a projection definition.                                    */
  /* -------------------------------------------------------------------- */
      pszProjection = GTIFGetProj4Defn( psDefn );
  
      if( pszProjection == NULL )
          return FALSE;
  
  /* -------------------------------------------------------------------- */
  /*      Parse into tokens for pj_init(), and initialize the projection. */
  /* -------------------------------------------------------------------- */
      
      papszArgs = CSLTokenizeStringComplex( pszProjection, " +", TRUE, FALSE );
      free( pszProjection );
  
      psPJ = pj_init( CSLCount(papszArgs), papszArgs );
  
      CSLDestroy( papszArgs );
  
      if( psPJ == NULL )
      {
          return FALSE;
      }
  
  /* -------------------------------------------------------------------- */
  /*      Process each of the points.                                     */
  /* -------------------------------------------------------------------- */
      for( i = 0; i < nPoints; i++ )
      {
          projUV	sUV;
  
          sUV.u = padfX[i];
          sUV.v = padfY[i];
  
          sUV = pj_inv( sUV, psPJ );
  
          padfX[i] = sUV.u * RAD_TO_DEG;
          padfY[i] = sUV.v * RAD_TO_DEG;
      }
  
      pj_free( psPJ );
  
      return TRUE;
  }
  
  #endif /* has projects.h and -lproj */
  
  