LCOV - code coverage report
Current view: top level - frmts/gtiff/libgeotiff - geo_normalize.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 916 1357 67.5 %
Date: 2025-01-18 12:42:00 Functions: 26 33 78.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  libgeotiff
       4             :  * Purpose:  Code to normalize PCS and other composite codes in a GeoTIFF file.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2018, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *****************************************************************************/
      13             : 
      14             : #include <assert.h>
      15             : #include <stdio.h>
      16             : #include <stdlib.h>
      17             : #include <string.h>
      18             : 
      19             : #include "cpl_serv.h"
      20             : #include "geo_tiffp.h"
      21             : #include "geovalues.h"
      22             : #include "geo_normalize.h"
      23             : #include "geo_keyp.h"
      24             : 
      25             : #include "proj.h"
      26             : 
      27             : #ifndef KvUserDefined
      28             : #  define KvUserDefined 32767
      29             : #endif
      30             : 
      31             : #ifndef M_PI
      32             : #  define M_PI 3.14159265358979323846
      33             : #endif
      34             : 
      35             : /* EPSG Codes for projection parameters.  Unfortunately, these bear no
      36             :    relationship to the GeoTIFF codes even though the names are so similar. */
      37             : 
      38             : #define EPSGNatOriginLat         8801
      39             : #define EPSGNatOriginLong        8802
      40             : #define EPSGNatOriginScaleFactor 8805
      41             : #define EPSGFalseEasting         8806
      42             : #define EPSGFalseNorthing        8807
      43             : #define EPSGProjCenterLat        8811
      44             : #define EPSGProjCenterLong       8812
      45             : #define EPSGAzimuth              8813
      46             : #define EPSGAngleRectifiedToSkewedGrid 8814
      47             : #define EPSGInitialLineScaleFactor 8815
      48             : #define EPSGProjCenterEasting    8816
      49             : #define EPSGProjCenterNorthing   8817
      50             : #define EPSGPseudoStdParallelLat 8818
      51             : #define EPSGPseudoStdParallelScaleFactor 8819
      52             : #define EPSGFalseOriginLat       8821
      53             : #define EPSGFalseOriginLong      8822
      54             : #define EPSGStdParallel1Lat      8823
      55             : #define EPSGStdParallel2Lat      8824
      56             : #define EPSGFalseOriginEasting   8826
      57             : #define EPSGFalseOriginNorthing  8827
      58             : #define EPSGSphericalOriginLat   8828
      59             : #define EPSGSphericalOriginLong  8829
      60             : #define EPSGInitialLongitude     8830
      61             : #define EPSGZoneWidth            8831
      62             : #define EPSGLatOfStdParallel     8832
      63             : #define EPSGOriginLong           8833
      64             : #define EPSGTopocentricOriginLat 8834
      65             : #define EPSGTopocentricOriginLong 8835
      66             : #define EPSGTopocentricOriginHeight 8836
      67             : 
      68             : #define CT_Ext_Mercator_2SP     -CT_Mercator
      69             : 
      70             : #ifndef CPL_INLINE
      71             : #  if (defined(__GNUC__) && !defined(__NO_INLINE__)) || defined(_MSC_VER)
      72             : #    define HAS_CPL_INLINE  1
      73             : #    define CPL_INLINE __inline
      74             : #  elif defined(__SUNPRO_CC)
      75             : #    define HAS_CPL_INLINE  1
      76             : #    define CPL_INLINE inline
      77             : #  else
      78             : #    define CPL_INLINE
      79             : #  endif
      80             : #endif
      81             : 
      82             : #ifndef CPL_UNUSED
      83             : #if defined(__GNUC__) && __GNUC__ >= 4
      84             : #  define CPL_UNUSED __attribute((__unused__))
      85             : #else
      86             : /* TODO: add cases for other compilers */
      87             : #  define CPL_UNUSED
      88             : #endif
      89             : #endif
      90             : 
      91       19549 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {}
      92             : 
      93             : /************************************************************************/
      94             : /*                         GTIFKeyGetSSHORT()                           */
      95             : /************************************************************************/
      96             : 
      97             : // Geotiff SHORT keys are supposed to be unsigned, but geo_normalize interface
      98             : // uses signed short...
      99       80197 : static int GTIFKeyGetSSHORT( GTIF *gtif, geokey_t key, short* pnVal )
     100             : {
     101             :     unsigned short sVal;
     102       80197 :     if( GTIFKeyGetSHORT(gtif, key, &sVal, 0, 1) == 1 )
     103             :     {
     104       24155 :         memcpy(pnVal, &sVal, 2);
     105       24155 :         return 1;
     106             :     }
     107       56042 :     return 0;
     108             : }
     109             : 
     110             : /************************************************************************/
     111             : /*                           GTIFGetPCSInfo()                           */
     112             : /************************************************************************/
     113             : 
     114        6410 : int GTIFGetPCSInfoEx( void* ctxIn,
     115             :                       int nPCSCode, char **ppszEPSGName,
     116             :                       short *pnProjOp, short *pnUOMLengthCode,
     117             :                       short *pnGeogCS )
     118             : 
     119             : {
     120             :     int         nDatum;
     121             :     int         nZone;
     122             : 
     123             :     /* Deal with a few well known CRS */
     124        6410 :     int Proj = GTIFPCSToMapSys( nPCSCode, &nDatum, &nZone );
     125        6410 :     if ((Proj == MapSys_UTM_North || Proj == MapSys_UTM_South) &&
     126        6076 :         nDatum != KvUserDefined)
     127             :     {
     128        6076 :         const char* pszDatumName = NULL;
     129        6076 :         switch (nDatum)
     130             :         {
     131        5726 :             case GCS_NAD27: pszDatumName = "NAD27"; break;
     132          12 :             case GCS_NAD83: pszDatumName = "NAD83"; break;
     133           0 :             case GCS_WGS_72: pszDatumName = "WGS 72"; break;
     134           0 :             case GCS_WGS_72BE: pszDatumName = "WGS 72BE"; break;
     135         338 :             case GCS_WGS_84: pszDatumName = "WGS 84"; break;
     136           0 :             default: break;
     137             :         }
     138             : 
     139        6076 :         if (pszDatumName)
     140             :         {
     141        6076 :             if (ppszEPSGName)
     142             :             {
     143             :                 char szEPSGName[64];
     144        3038 :                 snprintf(
     145             :                     szEPSGName, sizeof(szEPSGName), "%s / UTM zone %d%c",
     146             :                     pszDatumName, nZone,
     147             :                     (Proj == MapSys_UTM_North) ? 'N' : 'S');
     148        3038 :                 *ppszEPSGName = CPLStrdup(szEPSGName);
     149             :             }
     150             : 
     151        6076 :             if (pnProjOp)
     152        3038 :                 *pnProjOp = (short) (((Proj == MapSys_UTM_North) ? Proj_UTM_zone_1N - 1 : Proj_UTM_zone_1S - 1) + nZone);
     153             : 
     154        6076 :             if (pnUOMLengthCode)
     155        3038 :                 *pnUOMLengthCode = 9001; /* Linear_Meter */
     156             : 
     157        6076 :             if (pnGeogCS)
     158        3038 :                 *pnGeogCS = (short) nDatum;
     159             : 
     160        6076 :             return TRUE;
     161             :         }
     162             :     }
     163             : 
     164         334 :     if( nPCSCode == KvUserDefined )
     165           0 :         return FALSE;
     166             : 
     167         334 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
     168             :     {
     169             :         char szCode[12];
     170             : 
     171         334 :         snprintf(szCode, sizeof(szCode), "%d", nPCSCode);
     172         334 :         PJ* proj_crs = proj_create_from_database(
     173             :             ctx, "EPSG", szCode, PJ_CATEGORY_CRS, 0, NULL);
     174         334 :         if( !proj_crs )
     175             :         {
     176           0 :             return FALSE;
     177             :         }
     178             : 
     179         334 :         if( proj_get_type(proj_crs) != PJ_TYPE_PROJECTED_CRS )
     180             :         {
     181           0 :             proj_destroy(proj_crs);
     182           0 :             return FALSE;
     183             :         }
     184             : 
     185         334 :         if( ppszEPSGName )
     186             :         {
     187         167 :             const char* pszName = proj_get_name(proj_crs);
     188         167 :             if( !pszName )
     189             :             {
     190             :                 // shouldn't happen
     191           0 :                 proj_destroy(proj_crs);
     192           0 :                 return FALSE;
     193             :             }
     194         167 :             *ppszEPSGName = CPLStrdup(pszName);
     195             :         }
     196             : 
     197         334 :         if( pnProjOp )
     198             :         {
     199         167 :             PJ* conversion = proj_crs_get_coordoperation(
     200             :                 ctx, proj_crs);
     201         167 :             if( !conversion )
     202             :             {
     203             :                 // shouldn't happen except out of memory
     204           0 :                 proj_destroy(proj_crs);
     205           0 :                 return FALSE;
     206             :             }
     207             : 
     208             :             {
     209         167 :                 const char* pszConvCode = proj_get_id_code(conversion, 0);
     210         167 :                 assert( pszConvCode );
     211         167 :                 *pnProjOp = (short) atoi(pszConvCode);
     212             :             }
     213             : 
     214         167 :             proj_destroy(conversion);
     215             :         }
     216             : 
     217         334 :         if( pnUOMLengthCode )
     218             :         {
     219         167 :             PJ* coordSys = proj_crs_get_coordinate_system(
     220             :                 ctx, proj_crs);
     221         167 :             if( !coordSys )
     222             :             {
     223             :                 // shouldn't happen except out of memory
     224           0 :                 proj_destroy(proj_crs);
     225           0 :                 return FALSE;
     226             :             }
     227             : 
     228             :             {
     229         167 :                 const char* pszUnitCode = NULL;
     230         167 :                 if( !proj_cs_get_axis_info(
     231             :                     ctx, coordSys, 0,
     232             :                     NULL, /* name */
     233             :                     NULL, /* abbreviation*/
     234             :                     NULL, /* direction */
     235             :                     NULL, /* conversion factor */
     236             :                     NULL, /* unit name */
     237             :                     NULL, /* unit auth name (should be EPSG) */
     238         167 :                     &pszUnitCode) || pszUnitCode == NULL )
     239             :                 {
     240           0 :                     proj_destroy(coordSys);
     241           0 :                     return FALSE;
     242             :                 }
     243         167 :                 *pnUOMLengthCode = (short) atoi(pszUnitCode);
     244         167 :                 proj_destroy(coordSys);
     245             :             }
     246             :         }
     247             : 
     248         334 :         if( pnGeogCS )
     249             :         {
     250         167 :             PJ* geod_crs = proj_crs_get_geodetic_crs(ctx, proj_crs);
     251         167 :             if( !geod_crs )
     252             :             {
     253             :                 // shouldn't happen except out of memory
     254           0 :                 proj_destroy(proj_crs);
     255           0 :                 return FALSE;
     256             :             }
     257             : 
     258             :             {
     259         167 :                 const char* pszGeodCode = proj_get_id_code(geod_crs, 0);
     260         167 :                 assert( pszGeodCode );
     261         167 :                 *pnGeogCS = (short) atoi(pszGeodCode);
     262             :             }
     263             : 
     264         167 :             proj_destroy(geod_crs);
     265             :         }
     266             : 
     267             : 
     268         334 :         proj_destroy(proj_crs);
     269         334 :         return TRUE;
     270             :     }
     271             : }
     272             : 
     273             : 
     274           0 : int GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName,
     275             :                     short *pnProjOp, short *pnUOMLengthCode,
     276             :                     short *pnGeogCS )
     277             : 
     278             : {
     279           0 :     PJ_CONTEXT* ctx = proj_context_create();
     280           0 :     int ret = GTIFGetPCSInfoEx(ctx, nPCSCode, ppszEPSGName, pnProjOp,
     281             :                                pnUOMLengthCode, pnGeogCS);
     282           0 :     proj_context_destroy(ctx);
     283           0 :     return ret;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                           GTIFAngleToDD()                            */
     288             : /*                                                                      */
     289             : /*      Convert a numeric angle to decimal degrees.                     */
     290             : /************************************************************************/
     291             : 
     292         435 : double GTIFAngleToDD( double dfAngle, int nUOMAngle )
     293             : 
     294             : {
     295         435 :     if( nUOMAngle == 9110 )   /* DDD.MMSSsss */
     296             :     {
     297           0 :         if( dfAngle > -999.9 && dfAngle < 999.9 )
     298             :         {
     299             :             char  szAngleString[32];
     300             : 
     301           0 :             snprintf(szAngleString, sizeof(szAngleString), "%12.7f", dfAngle);
     302           0 :             dfAngle = GTIFAngleStringToDD( szAngleString, nUOMAngle );
     303             :         }
     304             :     }
     305         435 :     else if ( nUOMAngle != KvUserDefined )
     306             :     {
     307         401 :         double    dfInDegrees = 1.0;
     308             : 
     309         401 :         GTIFGetUOMAngleInfo( nUOMAngle, NULL, &dfInDegrees );
     310         401 :         dfAngle = dfAngle * dfInDegrees;
     311             :     }
     312             : 
     313         435 :     return dfAngle;
     314             : }
     315             : 
     316             : /************************************************************************/
     317             : /*                        GTIFAngleStringToDD()                         */
     318             : /*                                                                      */
     319             : /*      Convert an angle in the specified units to decimal degrees.     */
     320             : /************************************************************************/
     321             : 
     322           0 : double GTIFAngleStringToDD( const char * pszAngle, int nUOMAngle )
     323             : 
     324             : {
     325             :     double  dfAngle;
     326             : 
     327           0 :     if( nUOMAngle == 9110 )   /* DDD.MMSSsss */
     328             :     {
     329             :         char  *pszDecimal;
     330             : 
     331           0 :         dfAngle = ABS(atoi(pszAngle));
     332           0 :         pszDecimal = strchr(pszAngle,'.');
     333           0 :         if( pszDecimal != NULL && strlen(pszDecimal) > 1 )
     334             :         {
     335             :             char  szMinutes[3];
     336             :             char  szSeconds[64];
     337             : 
     338           0 :             szMinutes[0] = pszDecimal[1];
     339           0 :             if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' )
     340           0 :                 szMinutes[1] = pszDecimal[2];
     341             :             else
     342           0 :                 szMinutes[1] = '0';
     343             : 
     344           0 :             szMinutes[2] = '\0';
     345           0 :             dfAngle += atoi(szMinutes) / 60.0;
     346             : 
     347           0 :             if( strlen(pszDecimal) > 3 )
     348             :             {
     349           0 :                 szSeconds[0] = pszDecimal[3];
     350           0 :                 if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' )
     351             :                 {
     352           0 :                     szSeconds[1] = pszDecimal[4];
     353           0 :                     szSeconds[2] = '.';
     354           0 :                     strncpy( szSeconds+3, pszDecimal + 5, sizeof(szSeconds) - 3 );
     355           0 :                     szSeconds[sizeof(szSeconds) - 1] = 0;
     356             :                 }
     357             :                 else
     358             :                 {
     359           0 :                     szSeconds[1] = '0';
     360           0 :                     szSeconds[2] = '\0';
     361             :                 }
     362           0 :                 dfAngle += GTIFAtof(szSeconds) / 3600.0;
     363             :             }
     364             :         }
     365             : 
     366           0 :         if( pszAngle[0] == '-' )
     367           0 :             dfAngle *= -1;
     368             :     }
     369           0 :     else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */
     370             :     {
     371           0 :         dfAngle = 180 * (GTIFAtof(pszAngle ) / 200);
     372             :     }
     373           0 :     else if( nUOMAngle == 9101 )      /* radians */
     374             :     {
     375           0 :         dfAngle = 180 * (GTIFAtof(pszAngle ) / M_PI);
     376             :     }
     377           0 :     else if( nUOMAngle == 9103 )      /* arc-minute */
     378             :     {
     379           0 :         dfAngle = GTIFAtof(pszAngle) / 60;
     380             :     }
     381           0 :     else if( nUOMAngle == 9104 )      /* arc-second */
     382             :     {
     383           0 :         dfAngle = GTIFAtof(pszAngle) / 3600;
     384             :     }
     385             :     else /* decimal degrees ... some cases missing but seemingly never used */
     386             :     {
     387           0 :         CPLAssert( nUOMAngle == 9102 || nUOMAngle == KvUserDefined
     388             :                    || nUOMAngle == 0 );
     389             : 
     390           0 :         dfAngle = GTIFAtof(pszAngle );
     391             :     }
     392             : 
     393           0 :     return dfAngle;
     394             : }
     395             : 
     396             : /************************************************************************/
     397             : /*                           GTIFGetGCSInfo()                           */
     398             : /*                                                                      */
     399             : /*      Fetch the datum, and prime meridian related to a particular     */
     400             : /*      GCS.                                                            */
     401             : /************************************************************************/
     402             : 
     403       14405 : int GTIFGetGCSInfoEx( void* ctxIn,
     404             :                       int nGCSCode, char ** ppszName,
     405             :                       short * pnDatum, short * pnPM, short *pnUOMAngle )
     406             : 
     407             : {
     408       14405 :     int   nDatum=0;
     409             : 
     410             : /* -------------------------------------------------------------------- */
     411             : /*      Handle some "well known" GCS codes directly                     */
     412             : /* -------------------------------------------------------------------- */
     413       14405 :     const char * pszName = NULL;
     414       14405 :     const int nPM = PM_Greenwich;
     415       14405 :     const int nUOMAngle = Angular_DMS_Hemisphere;
     416       14405 :     if( nGCSCode == GCS_NAD27 )
     417             :     {
     418        5780 :         nDatum = Datum_North_American_Datum_1927;
     419        5780 :         pszName = "NAD27";
     420             :     }
     421        8625 :     else if( nGCSCode == GCS_NAD83 )
     422             :     {
     423          30 :         nDatum = Datum_North_American_Datum_1983;
     424          30 :         pszName = "NAD83";
     425             :     }
     426        8595 :     else if( nGCSCode == GCS_WGS_84 )
     427             :     {
     428        8287 :         nDatum = Datum_WGS84;
     429        8287 :         pszName = "WGS 84";
     430             :     }
     431         308 :     else if( nGCSCode == GCS_WGS_72 )
     432             :     {
     433          10 :         nDatum = Datum_WGS72;
     434          10 :         pszName = "WGS 72";
     435             :     }
     436         298 :     else if ( nGCSCode == KvUserDefined )
     437             :     {
     438         100 :         return FALSE;
     439             :     }
     440             : 
     441       14305 :     if (pszName != NULL)
     442             :     {
     443       14107 :         if( ppszName != NULL )
     444        7049 :             *ppszName = CPLStrdup( pszName );
     445       14107 :         if( pnDatum != NULL )
     446        7058 :             *pnDatum = (short) nDatum;
     447       14107 :         if( pnPM != NULL )
     448        7058 :             *pnPM = (short) nPM;
     449       14107 :         if( pnUOMAngle != NULL )
     450        7058 :             *pnUOMAngle = (short) nUOMAngle;
     451             : 
     452       14107 :         return TRUE;
     453             :     }
     454             : 
     455             : /* -------------------------------------------------------------------- */
     456             : /*      Search the database.                                            */
     457             : /* -------------------------------------------------------------------- */
     458             : 
     459         198 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
     460             :     {
     461             :         char szCode[12];
     462             : 
     463         198 :         snprintf(szCode, sizeof(szCode), "%d", nGCSCode);
     464         198 :         PJ* geod_crs = proj_create_from_database(
     465             :             ctx, "EPSG", szCode, PJ_CATEGORY_CRS, 0, NULL);
     466         198 :         if( !geod_crs )
     467             :         {
     468           0 :             return FALSE;
     469             :         }
     470             : 
     471             :         {
     472         198 :             const int objType = proj_get_type(geod_crs);
     473         198 :             if( objType != PJ_TYPE_GEODETIC_CRS &&
     474         198 :                 objType != PJ_TYPE_GEOCENTRIC_CRS &&
     475           2 :                 objType != PJ_TYPE_GEOGRAPHIC_2D_CRS &&
     476             :                 objType != PJ_TYPE_GEOGRAPHIC_3D_CRS )
     477             :             {
     478           0 :                 proj_destroy(geod_crs);
     479           0 :                 return FALSE;
     480             :             }
     481             :         }
     482             : 
     483         198 :         if( ppszName )
     484             :         {
     485          99 :             pszName = proj_get_name(geod_crs);
     486          99 :             if( !pszName )
     487             :             {
     488             :                 // shouldn't happen
     489           0 :                 proj_destroy(geod_crs);
     490           0 :                 return FALSE;
     491             :             }
     492          99 :             *ppszName = CPLStrdup(pszName);
     493             :         }
     494             : 
     495         198 :         if( pnDatum )
     496             :         {
     497             : #if PROJ_VERSION_MAJOR >= 8
     498             :             PJ* datum = proj_crs_get_datum_forced(ctx, geod_crs);
     499             : #else
     500          99 :             PJ* datum = proj_crs_get_datum(ctx, geod_crs);
     501             : #endif
     502          99 :             if( !datum )
     503             :             {
     504           0 :                 proj_destroy(geod_crs);
     505           0 :                 return FALSE;
     506             :             }
     507             : 
     508             :             {
     509          99 :                 const char* pszDatumCode = proj_get_id_code(datum, 0);
     510          99 :                 assert( pszDatumCode );
     511          99 :                 *pnDatum = (short) atoi(pszDatumCode);
     512             :             }
     513             : 
     514          99 :             proj_destroy(datum);
     515             :         }
     516             : 
     517         198 :         if( pnPM )
     518             :         {
     519          99 :             PJ* pm = proj_get_prime_meridian(ctx, geod_crs);
     520          99 :             if( !pm )
     521             :             {
     522           0 :                 proj_destroy(geod_crs);
     523           0 :                 return FALSE;
     524             :             }
     525             : 
     526             :             {
     527          99 :                 const char* pszPMCode = proj_get_id_code(pm, 0);
     528          99 :                 assert( pszPMCode );
     529          99 :                 *pnPM = (short) atoi(pszPMCode);
     530             :             }
     531             : 
     532          99 :             proj_destroy(pm);
     533             :         }
     534             : 
     535         198 :         if( pnUOMAngle )
     536             :         {
     537          99 :             PJ* coordSys = proj_crs_get_coordinate_system(
     538             :                 ctx, geod_crs);
     539          99 :             if( !coordSys )
     540             :             {
     541             :                 // shouldn't happen except out of memory
     542           0 :                 proj_destroy(geod_crs);
     543           0 :                 return FALSE;
     544             :             }
     545             : 
     546             :             {
     547          99 :                 const char* pszUnitCode = NULL;
     548          99 :                 if( !proj_cs_get_axis_info(
     549             :                     ctx, coordSys, 0,
     550             :                     NULL, /* name */
     551             :                     NULL, /* abbreviation*/
     552             :                     NULL, /* direction */
     553             :                     NULL, /* conversion factor */
     554             :                     NULL, /* unit name */
     555             :                     NULL, /* unit auth name (should be EPSG) */
     556          99 :                     &pszUnitCode) || pszUnitCode == NULL )
     557             :                 {
     558           0 :                     proj_destroy(coordSys);
     559           0 :                     return FALSE;
     560             :                 }
     561          99 :                 *pnUOMAngle = (short) atoi(pszUnitCode);
     562          99 :                 proj_destroy(coordSys);
     563             :             }
     564             :         }
     565             : 
     566         198 :         proj_destroy(geod_crs);
     567         198 :         return TRUE;
     568             :     }
     569             : }
     570             : 
     571          20 : int GTIFGetGCSInfo( int nGCSCode, char ** ppszName,
     572             :                     short * pnDatum, short * pnPM, short *pnUOMAngle )
     573             : 
     574             : {
     575          20 :     PJ_CONTEXT* ctx = proj_context_create();
     576          20 :     const int ret = GTIFGetGCSInfoEx(ctx, nGCSCode, ppszName, pnDatum,
     577             :                                      pnPM, pnUOMAngle);
     578          20 :     proj_context_destroy(ctx);
     579          20 :     return ret;
     580             : }
     581             : 
     582             : /************************************************************************/
     583             : /*                        GTIFGetEllipsoidInfo()                        */
     584             : /*                                                                      */
     585             : /*      Fetch info about an ellipsoid.  Axes are always returned in     */
     586             : /*      meters.  SemiMajor computed based on inverse flattening         */
     587             : /*      where that is provided.                                         */
     588             : /************************************************************************/
     589             : 
     590       14365 : int GTIFGetEllipsoidInfoEx( void* ctxIn,
     591             :                             int nEllipseCode, char ** ppszName,
     592             :                             double * pdfSemiMajor, double * pdfSemiMinor )
     593             : 
     594             : {
     595       14365 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
     596             : /* -------------------------------------------------------------------- */
     597             : /*      Try some well known ellipsoids.                                 */
     598             : /* -------------------------------------------------------------------- */
     599       14365 :     double  dfSemiMajor=0.0;
     600       14365 :     double     dfInvFlattening=0.0, dfSemiMinor=0.0;
     601       14365 :     const char *pszName = NULL;
     602             : 
     603       14365 :     if( nEllipseCode == Ellipse_Clarke_1866 )
     604             :     {
     605        5784 :         pszName = "Clarke 1866";
     606        5784 :         dfSemiMajor = 6378206.4;
     607        5784 :         dfSemiMinor = 6356583.8;
     608        5784 :         dfInvFlattening = 0.0;
     609             :     }
     610        8581 :     else if( nEllipseCode == Ellipse_GRS_1980 )
     611             :     {
     612         143 :         pszName = "GRS 1980";
     613         143 :         dfSemiMajor = 6378137.0;
     614         143 :         dfSemiMinor = 0.0;
     615         143 :         dfInvFlattening = 298.257222101;
     616             :     }
     617        8438 :     else if( nEllipseCode == Ellipse_WGS_84 )
     618             :     {
     619        8319 :         pszName = "WGS 84";
     620        8319 :         dfSemiMajor = 6378137.0;
     621        8319 :         dfSemiMinor = 0.0;
     622        8319 :         dfInvFlattening = 298.257223563;
     623             :     }
     624         119 :     else if( nEllipseCode == 7043 )
     625             :     {
     626          10 :         pszName = "WGS 72";
     627          10 :         dfSemiMajor = 6378135.0;
     628          10 :         dfSemiMinor = 0.0;
     629          10 :         dfInvFlattening = 298.26;
     630             :     }
     631             : 
     632       14365 :     if (pszName != NULL)
     633             :     {
     634       14256 :         if( dfSemiMinor == 0.0 )
     635        8472 :             dfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening);
     636             : 
     637       14256 :         if( pdfSemiMinor != NULL )
     638        7133 :             *pdfSemiMinor = dfSemiMinor;
     639       14256 :         if( pdfSemiMajor != NULL )
     640        7133 :             *pdfSemiMajor = dfSemiMajor;
     641       14256 :         if( ppszName != NULL )
     642        7123 :             *ppszName = CPLStrdup( pszName );
     643             : 
     644       14256 :         return TRUE;
     645             :     }
     646             : 
     647         109 :     if( nEllipseCode == KvUserDefined )
     648          13 :         return FALSE;
     649             : 
     650             : /* -------------------------------------------------------------------- */
     651             : /*      Search the database.                                            */
     652             : /* -------------------------------------------------------------------- */
     653             :     {
     654             :         char szCode[12];
     655             : 
     656          96 :         snprintf(szCode, sizeof(szCode), "%d", nEllipseCode);
     657          96 :         PJ* ellipsoid = proj_create_from_database(
     658             :             ctx, "EPSG", szCode, PJ_CATEGORY_ELLIPSOID, 0, NULL);
     659          96 :         if( !ellipsoid )
     660             :         {
     661           3 :             return FALSE;
     662             :         }
     663             : 
     664          93 :         if( ppszName )
     665             :         {
     666          46 :             pszName = proj_get_name(ellipsoid);
     667          46 :             if( !pszName )
     668             :             {
     669             :                 // shouldn't happen
     670           0 :                 proj_destroy(ellipsoid);
     671           0 :                 return FALSE;
     672             :             }
     673          46 :             *ppszName = CPLStrdup(pszName);
     674             :         }
     675             : 
     676          93 :         proj_ellipsoid_get_parameters(
     677             :             ctx, ellipsoid, pdfSemiMajor, pdfSemiMinor, NULL, NULL);
     678             : 
     679          93 :         proj_destroy(ellipsoid);
     680             : 
     681          93 :         return TRUE;
     682             :     }
     683             : }
     684             : 
     685          20 : int GTIFGetEllipsoidInfo( int nEllipseCode, char ** ppszName,
     686             :                           double * pdfSemiMajor, double * pdfSemiMinor )
     687             : 
     688             : {
     689          20 :     PJ_CONTEXT* ctx = proj_context_create();
     690          20 :     const int ret = GTIFGetEllipsoidInfoEx(ctx, nEllipseCode, ppszName, pdfSemiMajor,
     691             :                                            pdfSemiMinor);
     692          20 :     proj_context_destroy(ctx);
     693          20 :     return ret;
     694             : }
     695             : 
     696             : /************************************************************************/
     697             : /*                           GTIFGetPMInfo()                            */
     698             : /*                                                                      */
     699             : /*      Get the offset between a given prime meridian and Greenwich     */
     700             : /*      in degrees.                                                     */
     701             : /************************************************************************/
     702             : 
     703       14322 : int GTIFGetPMInfoEx( void* ctxIn,
     704             :                      int nPMCode, char ** ppszName, double *pdfOffset )
     705             : 
     706             : {
     707             : /* -------------------------------------------------------------------- */
     708             : /*      Use a special short cut for Greenwich, since it is so common.   */
     709             : /* -------------------------------------------------------------------- */
     710       14322 :     if( nPMCode == PM_Greenwich )
     711             :     {
     712       14287 :         if( pdfOffset != NULL )
     713        7148 :             *pdfOffset = 0.0;
     714       14287 :         if( ppszName != NULL )
     715        7139 :             *ppszName = CPLStrdup( "Greenwich" );
     716       14287 :         return TRUE;
     717             :     }
     718             : 
     719             : 
     720          35 :     if( nPMCode == KvUserDefined )
     721          13 :         return FALSE;
     722             : 
     723             : /* -------------------------------------------------------------------- */
     724             : /*      Search the database.                                            */
     725             : /* -------------------------------------------------------------------- */
     726          22 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
     727             :     {
     728             :         char szCode[12];
     729             : 
     730          22 :         snprintf(szCode, sizeof(szCode), "%d", nPMCode);
     731          22 :         PJ* pm = proj_create_from_database(
     732             :             ctx, "EPSG", szCode, PJ_CATEGORY_PRIME_MERIDIAN, 0, NULL);
     733          22 :         if( !pm )
     734             :         {
     735           0 :             return FALSE;
     736             :         }
     737             : 
     738          22 :         if( ppszName )
     739             :         {
     740          11 :             const char* pszName = proj_get_name(pm);
     741          11 :             if( !pszName )
     742             :             {
     743             :                 // shouldn't happen
     744           0 :                 proj_destroy(pm);
     745           0 :                 return FALSE;
     746             :             }
     747          11 :             *ppszName = CPLStrdup(pszName);
     748             :         }
     749             : 
     750          22 :         if( pdfOffset )
     751             :         {
     752          11 :             double conv_factor = 0;
     753          11 :             proj_prime_meridian_get_parameters(
     754             :                 ctx, pm, pdfOffset, &conv_factor, NULL);
     755          11 :             *pdfOffset *= conv_factor * 180.0 / M_PI;
     756             :         }
     757             : 
     758          22 :         proj_destroy(pm);
     759             : 
     760          22 :         return TRUE;
     761             :     }
     762             : }
     763             : 
     764          20 : int GTIFGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset )
     765             : 
     766             : {
     767          20 :     PJ_CONTEXT* ctx = proj_context_create();
     768          20 :     const int ret = GTIFGetPMInfoEx(ctx, nPMCode, ppszName, pdfOffset);
     769          20 :     proj_context_destroy(ctx);
     770          20 :     return ret;
     771             : }
     772             : 
     773             : /************************************************************************/
     774             : /*                          GTIFGetDatumInfo()                          */
     775             : /*                                                                      */
     776             : /*      Fetch the ellipsoid, and name for a datum.                      */
     777             : /************************************************************************/
     778             : 
     779       14361 : int GTIFGetDatumInfoEx( void* ctxIn,
     780             :                         int nDatumCode, char ** ppszName, short * pnEllipsoid )
     781             : 
     782             : {
     783       14361 :     const char* pszName = NULL;
     784       14361 :     int   nEllipsoid = 0;
     785             : 
     786             : /* -------------------------------------------------------------------- */
     787             : /*      Handle a few built-in datums.                                   */
     788             : /* -------------------------------------------------------------------- */
     789       14361 :     if( nDatumCode == Datum_North_American_Datum_1927 )
     790             :     {
     791        5782 :         nEllipsoid = Ellipse_Clarke_1866;
     792        5782 :         pszName = "North American Datum 1927";
     793             :     }
     794        8579 :     else if( nDatumCode == Datum_North_American_Datum_1983 )
     795             :     {
     796          34 :         nEllipsoid = Ellipse_GRS_1980;
     797          34 :         pszName = "North American Datum 1983";
     798             :     }
     799        8545 :     else if( nDatumCode == Datum_WGS84 )
     800             :     {
     801        8318 :         nEllipsoid = Ellipse_WGS_84;
     802        8318 :         pszName = "World Geodetic System 1984";
     803             :     }
     804         227 :     else if( nDatumCode == Datum_WGS72 )
     805             :     {
     806          10 :         nEllipsoid = 7043; /* WGS72 */
     807          10 :         pszName = "World Geodetic System 1972";
     808             :     }
     809             : 
     810       14361 :     if (pszName != NULL)
     811             :     {
     812       14144 :         if( pnEllipsoid != NULL )
     813        7077 :             *pnEllipsoid = (short) nEllipsoid;
     814             : 
     815       14144 :         if( ppszName != NULL )
     816        7067 :             *ppszName = CPLStrdup( pszName );
     817             : 
     818       14144 :         return TRUE;
     819             :     }
     820             : 
     821         217 :     if( nDatumCode == KvUserDefined )
     822          13 :         return FALSE;
     823             : 
     824             : /* -------------------------------------------------------------------- */
     825             : /*      Search the database.                                            */
     826             : /* -------------------------------------------------------------------- */
     827         204 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
     828             :     {
     829             :         char szCode[12];
     830             : 
     831         204 :         snprintf(szCode, sizeof(szCode), "%d", nDatumCode);
     832         204 :         PJ* datum = proj_create_from_database(
     833             :             ctx, "EPSG", szCode, PJ_CATEGORY_DATUM, 0, NULL);
     834         204 :         if( !datum )
     835             :         {
     836           2 :             return FALSE;
     837             :         }
     838             : 
     839         202 :         const PJ_TYPE pjType = proj_get_type(datum);
     840         202 :         if( pjType != PJ_TYPE_GEODETIC_REFERENCE_FRAME &&
     841             :             pjType != PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME )
     842             :         {
     843           0 :             proj_destroy(datum);
     844           0 :             return FALSE;
     845             :         }
     846             : 
     847         202 :         if( ppszName )
     848             :         {
     849         101 :             pszName = proj_get_name(datum);
     850         101 :             if( !pszName )
     851             :             {
     852             :                 // shouldn't happen
     853           0 :                 proj_destroy(datum);
     854           0 :                 return FALSE;
     855             :             }
     856         101 :             *ppszName = CPLStrdup(pszName);
     857             :         }
     858             : 
     859         202 :         if( pnEllipsoid )
     860             :         {
     861         101 :             PJ* ellipsoid = proj_get_ellipsoid(ctx, datum);
     862         101 :             if( !ellipsoid )
     863             :             {
     864           0 :                 proj_destroy(datum);
     865           0 :                 return FALSE;
     866             :             }
     867             : 
     868             :             {
     869         101 :                 const char* pszEllipsoidCode = proj_get_id_code(
     870             :                     ellipsoid, 0);
     871         101 :                 assert( pszEllipsoidCode );
     872         101 :                 *pnEllipsoid = (short) atoi(pszEllipsoidCode);
     873             :             }
     874             : 
     875         101 :             proj_destroy(ellipsoid);
     876             :         }
     877             : 
     878         202 :         proj_destroy(datum);
     879             : 
     880         202 :         return TRUE;
     881             :     }
     882             : }
     883             : 
     884          20 : int GTIFGetDatumInfo( int nDatumCode, char ** ppszName, short * pnEllipsoid )
     885             : 
     886             : {
     887          20 :     PJ_CONTEXT* ctx = proj_context_create();
     888          20 :     const int ret = GTIFGetDatumInfoEx(ctx, nDatumCode, ppszName, pnEllipsoid);
     889          20 :     proj_context_destroy(ctx);
     890          20 :     return ret;
     891             : }
     892             : 
     893             : /************************************************************************/
     894             : /*                        GTIFGetUOMLengthInfo()                        */
     895             : /*                                                                      */
     896             : /*      Note: This function should eventually also know how to          */
     897             : /*      lookup length aliases in the UOM_LE_ALIAS table.                */
     898             : /************************************************************************/
     899             : 
     900        7362 : int GTIFGetUOMLengthInfoEx( void* ctxIn,
     901             :                             int nUOMLengthCode,
     902             :                             char **ppszUOMName,
     903             :                             double * pdfInMeters )
     904             : 
     905             : {
     906             : /* -------------------------------------------------------------------- */
     907             : /*      We short cut meter to save work and avoid failure for missing   */
     908             : /*      in the most common cases.               */
     909             : /* -------------------------------------------------------------------- */
     910        7362 :     if( nUOMLengthCode == 9001 )
     911             :     {
     912        7297 :         if( ppszUOMName != NULL )
     913        3679 :             *ppszUOMName = CPLStrdup( "metre" );
     914        7297 :         if( pdfInMeters != NULL )
     915        3618 :             *pdfInMeters = 1.0;
     916             : 
     917        7297 :         return TRUE;
     918             :     }
     919             : 
     920          65 :     if( nUOMLengthCode == 9002 )
     921             :     {
     922           0 :         if( ppszUOMName != NULL )
     923           0 :             *ppszUOMName = CPLStrdup( "foot" );
     924           0 :         if( pdfInMeters != NULL )
     925           0 :             *pdfInMeters = 0.3048;
     926             : 
     927           0 :         return TRUE;
     928             :     }
     929             : 
     930          65 :     if( nUOMLengthCode == 9003 )
     931             :     {
     932          55 :         if( ppszUOMName != NULL )
     933          34 :             *ppszUOMName = CPLStrdup( "US survey foot" );
     934          55 :         if( pdfInMeters != NULL )
     935          21 :             *pdfInMeters = 12.0 / 39.37;
     936             : 
     937          55 :         return TRUE;
     938             :     }
     939             : 
     940          10 :     if( nUOMLengthCode == KvUserDefined )
     941           8 :         return FALSE;
     942             : 
     943             : /* -------------------------------------------------------------------- */
     944             : /*      Search the units database for this unit.  If we don't find      */
     945             : /*      it return failure.                                              */
     946             : /* -------------------------------------------------------------------- */
     947             :     char szCode[12];
     948           2 :     const char* pszName = NULL;
     949             : 
     950           2 :     snprintf(szCode, sizeof(szCode), "%d", nUOMLengthCode);
     951           2 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
     952           2 :     if( !proj_uom_get_info_from_database(
     953             :         ctx, "EPSG", szCode, &pszName, pdfInMeters,  NULL) )
     954             :     {
     955           0 :         return FALSE;
     956             :     }
     957           2 :     if( ppszUOMName )
     958             :     {
     959           1 :         *ppszUOMName = CPLStrdup(pszName);
     960             :     }
     961           2 :     return TRUE;
     962             : }
     963             : 
     964         118 : int GTIFGetUOMLengthInfo( int nUOMLengthCode,
     965             :                           char **ppszUOMName,
     966             :                           double * pdfInMeters )
     967             : 
     968             : {
     969         118 :     PJ_CONTEXT* ctx = proj_context_create();
     970         118 :     const int ret = GTIFGetUOMLengthInfoEx(
     971             :         ctx, nUOMLengthCode, ppszUOMName, pdfInMeters);
     972         118 :     proj_context_destroy(ctx);
     973         118 :     return ret;
     974             : }
     975             : 
     976             : /************************************************************************/
     977             : /*                        GTIFGetUOMAngleInfo()                         */
     978             : /************************************************************************/
     979             : 
     980       15194 : int GTIFGetUOMAngleInfoEx( void* ctxIn,
     981             :                            int nUOMAngleCode,
     982             :                            char **ppszUOMName,
     983             :                            double * pdfInDegrees )
     984             : 
     985             : {
     986       15194 :     const char  *pszUOMName = NULL;
     987       15194 :     double  dfInDegrees = 1.0;
     988             : 
     989       15194 :     switch( nUOMAngleCode )
     990             :     {
     991           4 :       case 9101:
     992           4 :         pszUOMName = "radian";
     993           4 :         dfInDegrees = 180.0 / M_PI;
     994           4 :         break;
     995             : 
     996       15143 :       case 9102:
     997             :       case 9107:
     998             :       case 9108:
     999             :       case 9110:
    1000             :       case 9122:
    1001       15143 :         pszUOMName = "degree";
    1002       15143 :         dfInDegrees = 1.0;
    1003       15143 :         break;
    1004             : 
    1005           4 :       case 9103:
    1006           4 :         pszUOMName = "arc-minute";
    1007           4 :         dfInDegrees = 1 / 60.0;
    1008           4 :         break;
    1009             : 
    1010           6 :       case 9104:
    1011           6 :         pszUOMName = "arc-second";
    1012           6 :         dfInDegrees = 1 / 3600.0;
    1013           6 :         break;
    1014             : 
    1015          22 :       case 9105:
    1016          22 :         pszUOMName = "grad";
    1017          22 :         dfInDegrees = 180.0 / 200.0;
    1018          22 :         break;
    1019             : 
    1020           4 :       case 9106:
    1021           4 :         pszUOMName = "gon";
    1022           4 :         dfInDegrees = 180.0 / 200.0;
    1023           4 :         break;
    1024             : 
    1025           0 :       case 9109:
    1026           0 :         pszUOMName = "microradian";
    1027           0 :         dfInDegrees = 180.0 / (M_PI * 1000000.0);
    1028           0 :         break;
    1029             : 
    1030          11 :       default:
    1031          11 :         break;
    1032             :     }
    1033             : 
    1034       15194 :     if (pszUOMName)
    1035             :     {
    1036       15183 :         if( ppszUOMName != NULL )
    1037             :         {
    1038        7222 :             *ppszUOMName = CPLStrdup( pszUOMName );
    1039             :         }
    1040             : 
    1041       15183 :         if( pdfInDegrees != NULL )
    1042       15173 :             *pdfInDegrees = dfInDegrees;
    1043             : 
    1044       15183 :         return TRUE;
    1045             :     }
    1046             : 
    1047          11 :     if( nUOMAngleCode == KvUserDefined )
    1048          11 :         return FALSE;
    1049             : 
    1050             : /* -------------------------------------------------------------------- */
    1051             : /*      Search the units database for this unit.  If we don't find      */
    1052             : /*      it return failure.                                              */
    1053             : /* -------------------------------------------------------------------- */
    1054             :     char szCode[12];
    1055           0 :     const char* pszName = NULL;
    1056           0 :     double dfConvFactorToRadians = 0;
    1057             : 
    1058           0 :     snprintf(szCode, sizeof(szCode), "%d", nUOMAngleCode);
    1059           0 :     PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
    1060           0 :     if( !proj_uom_get_info_from_database(
    1061             :         ctx, "EPSG", szCode, &pszName, &dfConvFactorToRadians, NULL) )
    1062             :     {
    1063           0 :         return FALSE;
    1064             :     }
    1065           0 :     if( ppszUOMName )
    1066             :     {
    1067           0 :         *ppszUOMName = CPLStrdup(pszName);
    1068             :     }
    1069           0 :     if( pdfInDegrees )
    1070             :     {
    1071           0 :         *pdfInDegrees = dfConvFactorToRadians * 180.0 / M_PI;
    1072             :     }
    1073           0 :     return TRUE;
    1074             : }
    1075             : 
    1076         421 : int GTIFGetUOMAngleInfo( int nUOMAngleCode,
    1077             :                          char **ppszUOMName,
    1078             :                          double * pdfInDegrees )
    1079             : 
    1080             : {
    1081         421 :     PJ_CONTEXT* ctx = proj_context_create();
    1082         421 :     const int ret = GTIFGetUOMAngleInfoEx(
    1083             :         ctx, nUOMAngleCode, ppszUOMName, pdfInDegrees);
    1084         421 :     proj_context_destroy(ctx);
    1085         421 :     return ret;
    1086             : }
    1087             : 
    1088             : /************************************************************************/
    1089             : /*                    EPSGProjMethodToCTProjMethod()                    */
    1090             : /*                                                                      */
    1091             : /*      Convert between the EPSG enumeration for projection methods,    */
    1092             : /*      and the GeoTIFF CT codes.                                       */
    1093             : /************************************************************************/
    1094             : 
    1095        6594 : static int EPSGProjMethodToCTProjMethod( int nEPSG, int bReturnExtendedCTCode )
    1096             : 
    1097             : {
    1098        6594 :     switch( nEPSG )
    1099             :     {
    1100          21 :       case 9801:
    1101          21 :         return CT_LambertConfConic_1SP;
    1102             : 
    1103          12 :       case 9802:
    1104          12 :         return CT_LambertConfConic_2SP;
    1105             : 
    1106           0 :       case 9803:
    1107           0 :         return CT_LambertConfConic_2SP; /* Belgian variant not supported */
    1108             : 
    1109           3 :       case 9804:
    1110           3 :         return CT_Mercator;  /* 1SP and 2SP not differentiated */
    1111             : 
    1112           3 :       case 9805:
    1113           3 :         if( bReturnExtendedCTCode )
    1114           2 :             return CT_Ext_Mercator_2SP;
    1115             :         else
    1116           1 :             return CT_Mercator;  /* 1SP and 2SP not differentiated */
    1117             : 
    1118             :       /* Mercator 1SP (Spherical) For EPSG:3785 */
    1119           0 :       case 9841:
    1120           0 :         return CT_Mercator;  /* 1SP and 2SP not differentiated */
    1121             : 
    1122             :       /* Google Mercator For EPSG:3857 */
    1123         177 :       case 1024:
    1124         177 :         return CT_Mercator;  /* 1SP and 2SP not differentiated */
    1125             : 
    1126           3 :       case 9806:
    1127           3 :         return CT_CassiniSoldner;
    1128             : 
    1129        6300 :       case 9807:
    1130        6300 :         return CT_TransverseMercator;
    1131             : 
    1132           0 :       case 9808:
    1133           0 :         return CT_TransvMercator_SouthOriented;
    1134             : 
    1135          12 :       case 9809:
    1136          12 :         return CT_ObliqueStereographic;
    1137             : 
    1138          18 :       case 9810:
    1139             :       case 9829: /* variant B not quite the same - not sure how to handle */
    1140          18 :         return CT_PolarStereographic;
    1141             : 
    1142           3 :       case 9811:
    1143           3 :         return CT_NewZealandMapGrid;
    1144             : 
    1145           3 :       case 9812:
    1146           3 :         return CT_ObliqueMercator; /* is hotine actually different? */
    1147             : 
    1148           0 :       case 9813:
    1149           0 :         return CT_ObliqueMercator_Laborde;
    1150             : 
    1151           0 :       case 9814:
    1152           0 :         return CT_ObliqueMercator_Rosenmund; /* swiss  */
    1153             : 
    1154           6 :       case 9815:
    1155           6 :         return CT_HotineObliqueMercatorAzimuthCenter;
    1156             : 
    1157           0 :       case 9816: /* tunesia mining grid has no counterpart */
    1158           0 :         return KvUserDefined;
    1159             : 
    1160           3 :       case 9818:
    1161           3 :         return CT_Polyconic;
    1162             : 
    1163           3 :       case 9820:
    1164             :       case 1027:
    1165           3 :         return CT_LambertAzimEqualArea;
    1166             : 
    1167           3 :       case 9822:
    1168           3 :         return CT_AlbersEqualArea;
    1169             : 
    1170           0 :       case 9834:
    1171           0 :         return CT_CylindricalEqualArea;
    1172             : 
    1173           9 :       case 1028:
    1174             :       case 1029:
    1175             :       case 9823: /* spherical */
    1176             :       case 9842: /* elliptical */
    1177           9 :         return CT_Equirectangular;
    1178             : 
    1179          15 :       default: /* use the EPSG code for other methods */
    1180          15 :         return nEPSG;
    1181             :     }
    1182             : }
    1183             : 
    1184             : /************************************************************************/
    1185             : /*                           SetGTParamIds()                            */
    1186             : /*                                                                      */
    1187             : /*      This is hardcoded logic to set the GeoTIFF parameter            */
    1188             : /*      identifiers for all the EPSG supported projections.  As new     */
    1189             : /*      projection methods are added, this code will need to be updated */
    1190             : /************************************************************************/
    1191             : 
    1192        3375 : static int SetGTParamIds( int nCTProjection,
    1193             :                           int nEPSGProjMethod,
    1194             :                           int *panProjParamId,
    1195             :                           int *panEPSGCodes )
    1196             : 
    1197             : {
    1198             :     int anWorkingDummy[7];
    1199             : 
    1200        3375 :     if( panEPSGCodes == NULL )
    1201        3219 :         panEPSGCodes = anWorkingDummy;
    1202        3375 :     if( panProjParamId == NULL )
    1203         156 :         panProjParamId = anWorkingDummy;
    1204             : 
    1205        3375 :     memset( panEPSGCodes, 0, sizeof(int) * 7 );
    1206             : 
    1207             :     /* psDefn->nParms = 7; */
    1208             : 
    1209        3375 :     switch( nCTProjection )
    1210             :     {
    1211           6 :       case CT_CassiniSoldner:
    1212             :       case CT_NewZealandMapGrid:
    1213             :       case CT_Polyconic:
    1214           6 :         panProjParamId[0] = ProjNatOriginLatGeoKey;
    1215           6 :         panProjParamId[1] = ProjNatOriginLongGeoKey;
    1216           6 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1217           6 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1218             : 
    1219           6 :         panEPSGCodes[0] = EPSGNatOriginLat;
    1220           6 :         panEPSGCodes[1] = EPSGNatOriginLong;
    1221           6 :         panEPSGCodes[5] = EPSGFalseEasting;
    1222           6 :         panEPSGCodes[6] = EPSGFalseNorthing;
    1223           6 :         return TRUE;
    1224             : 
    1225           6 :       case CT_ObliqueMercator:
    1226             :       case CT_HotineObliqueMercatorAzimuthCenter:
    1227           6 :         panProjParamId[0] = ProjCenterLatGeoKey;
    1228           6 :         panProjParamId[1] = ProjCenterLongGeoKey;
    1229           6 :         panProjParamId[2] = ProjAzimuthAngleGeoKey;
    1230           6 :         panProjParamId[3] = ProjRectifiedGridAngleGeoKey;
    1231           6 :         panProjParamId[4] = ProjScaleAtCenterGeoKey;
    1232           6 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1233           6 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1234             : 
    1235           6 :         panEPSGCodes[0] = EPSGProjCenterLat;
    1236           6 :         panEPSGCodes[1] = EPSGProjCenterLong;
    1237           6 :         panEPSGCodes[2] = EPSGAzimuth;
    1238           6 :         panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid;
    1239           6 :         panEPSGCodes[4] = EPSGInitialLineScaleFactor;
    1240           6 :         panEPSGCodes[5] = EPSGProjCenterEasting; /* EPSG proj method 9812 uses EPSGFalseEasting, but 9815 uses EPSGProjCenterEasting */
    1241           6 :         panEPSGCodes[6] = EPSGProjCenterNorthing; /* EPSG proj method 9812 uses EPSGFalseNorthing, but 9815 uses EPSGProjCenterNorthing */
    1242           6 :         return TRUE;
    1243             : 
    1244           0 :       case CT_ObliqueMercator_Laborde:
    1245           0 :         panProjParamId[0] = ProjCenterLatGeoKey;
    1246           0 :         panProjParamId[1] = ProjCenterLongGeoKey;
    1247           0 :         panProjParamId[2] = ProjAzimuthAngleGeoKey;
    1248           0 :         panProjParamId[4] = ProjScaleAtCenterGeoKey;
    1249           0 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1250           0 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1251             : 
    1252           0 :         panEPSGCodes[0] = EPSGProjCenterLat;
    1253           0 :         panEPSGCodes[1] = EPSGProjCenterLong;
    1254           0 :         panEPSGCodes[2] = EPSGAzimuth;
    1255           0 :         panEPSGCodes[4] = EPSGInitialLineScaleFactor;
    1256           0 :         panEPSGCodes[5] = EPSGFalseEasting;
    1257           0 :         panEPSGCodes[6] = EPSGFalseNorthing;
    1258           0 :         return TRUE;
    1259             : 
    1260        3333 :       case CT_LambertConfConic_1SP:
    1261             :       case CT_Mercator:
    1262             :       case CT_ObliqueStereographic:
    1263             :       case CT_PolarStereographic:
    1264             :       case CT_TransverseMercator:
    1265             :       case CT_TransvMercator_SouthOriented:
    1266        3333 :         panProjParamId[0] = ProjNatOriginLatGeoKey;
    1267        3333 :         if( nCTProjection == CT_PolarStereographic )
    1268             :         {
    1269          12 :             panProjParamId[1] = ProjStraightVertPoleLongGeoKey;
    1270             :         }
    1271             :         else
    1272             :         {
    1273        3321 :             panProjParamId[1] = ProjNatOriginLongGeoKey;
    1274             :         }
    1275        3333 :         if( nEPSGProjMethod == 9805 ) /* Mercator_2SP */
    1276             :         {
    1277           0 :             panProjParamId[2] = ProjStdParallel1GeoKey;
    1278             :         }
    1279        3333 :         panProjParamId[4] = ProjScaleAtNatOriginGeoKey;
    1280        3333 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1281        3333 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1282             : 
    1283        3333 :         panEPSGCodes[0] = EPSGNatOriginLat;
    1284        3333 :         panEPSGCodes[1] = EPSGNatOriginLong;
    1285        3333 :         if( nEPSGProjMethod == 9805 ) /* Mercator_2SP */
    1286             :         {
    1287           0 :             panEPSGCodes[2] = EPSGStdParallel1Lat;
    1288             :         }
    1289        3333 :         panEPSGCodes[4] = EPSGNatOriginScaleFactor;
    1290        3333 :         panEPSGCodes[5] = EPSGFalseEasting;
    1291        3333 :         panEPSGCodes[6] = EPSGFalseNorthing;
    1292        3333 :         return TRUE;
    1293             : 
    1294           8 :       case CT_LambertConfConic_2SP:
    1295           8 :         panProjParamId[0] = ProjFalseOriginLatGeoKey;
    1296           8 :         panProjParamId[1] = ProjFalseOriginLongGeoKey;
    1297           8 :         panProjParamId[2] = ProjStdParallel1GeoKey;
    1298           8 :         panProjParamId[3] = ProjStdParallel2GeoKey;
    1299           8 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1300           8 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1301             : 
    1302           8 :         panEPSGCodes[0] = EPSGFalseOriginLat;
    1303           8 :         panEPSGCodes[1] = EPSGFalseOriginLong;
    1304           8 :         panEPSGCodes[2] = EPSGStdParallel1Lat;
    1305           8 :         panEPSGCodes[3] = EPSGStdParallel2Lat;
    1306           8 :         panEPSGCodes[5] = EPSGFalseOriginEasting;
    1307           8 :         panEPSGCodes[6] = EPSGFalseOriginNorthing;
    1308           8 :         return TRUE;
    1309             : 
    1310           2 :       case CT_AlbersEqualArea:
    1311           2 :         panProjParamId[0] = ProjStdParallel1GeoKey;
    1312           2 :         panProjParamId[1] = ProjStdParallel2GeoKey;
    1313           2 :         panProjParamId[2] = ProjNatOriginLatGeoKey;
    1314           2 :         panProjParamId[3] = ProjNatOriginLongGeoKey;
    1315           2 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1316           2 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1317             : 
    1318           2 :         panEPSGCodes[0] = EPSGStdParallel1Lat;
    1319           2 :         panEPSGCodes[1] = EPSGStdParallel2Lat;
    1320           2 :         panEPSGCodes[2] = EPSGFalseOriginLat;
    1321           2 :         panEPSGCodes[3] = EPSGFalseOriginLong;
    1322           2 :         panEPSGCodes[5] = EPSGFalseOriginEasting;
    1323           2 :         panEPSGCodes[6] = EPSGFalseOriginNorthing;
    1324           2 :         return TRUE;
    1325             : 
    1326           0 :       case CT_SwissObliqueCylindrical:
    1327           0 :         panProjParamId[0] = ProjCenterLatGeoKey;
    1328           0 :         panProjParamId[1] = ProjCenterLongGeoKey;
    1329           0 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1330           0 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1331             : 
    1332             :         /* EPSG codes? */
    1333           0 :         return TRUE;
    1334             : 
    1335           2 :       case CT_LambertAzimEqualArea:
    1336           2 :         panProjParamId[0] = ProjCenterLatGeoKey;
    1337           2 :         panProjParamId[1] = ProjCenterLongGeoKey;
    1338           2 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1339           2 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1340             : 
    1341           2 :         panEPSGCodes[0] = EPSGNatOriginLat;
    1342           2 :         panEPSGCodes[1] = EPSGNatOriginLong;
    1343           2 :         panEPSGCodes[5] = EPSGFalseEasting;
    1344           2 :         panEPSGCodes[6] = EPSGFalseNorthing;
    1345           2 :         return TRUE;
    1346             : 
    1347           0 :       case CT_CylindricalEqualArea:
    1348           0 :         panProjParamId[0] = ProjStdParallel1GeoKey;
    1349           0 :         panProjParamId[1] = ProjNatOriginLongGeoKey;
    1350           0 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1351           0 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1352             : 
    1353           0 :         panEPSGCodes[0] = EPSGStdParallel1Lat;
    1354           0 :         panEPSGCodes[1] = EPSGFalseOriginLong;
    1355           0 :         panEPSGCodes[5] = EPSGFalseOriginEasting;
    1356           0 :         panEPSGCodes[6] = EPSGFalseOriginNorthing;
    1357           0 :         return TRUE;
    1358             : 
    1359           6 :       case CT_Equirectangular:
    1360           6 :         panProjParamId[0] = ProjCenterLatGeoKey;
    1361           6 :         panProjParamId[1] = ProjCenterLongGeoKey;
    1362           6 :         panProjParamId[2] = ProjStdParallel1GeoKey;
    1363           6 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1364           6 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1365             : 
    1366           6 :         panEPSGCodes[0] = EPSGNatOriginLat;
    1367           6 :         panEPSGCodes[1] = EPSGNatOriginLong;
    1368           6 :         panEPSGCodes[2] = EPSGStdParallel1Lat;
    1369           6 :         panEPSGCodes[5] = EPSGFalseEasting;
    1370           6 :         panEPSGCodes[6] = EPSGFalseNorthing;
    1371           6 :         return TRUE;
    1372             : 
    1373           2 :       case CT_Ext_Mercator_2SP:
    1374           2 :         panProjParamId[0] = ProjNatOriginLatGeoKey;
    1375           2 :         panProjParamId[1] = ProjNatOriginLongGeoKey;
    1376           2 :         panProjParamId[2] = ProjStdParallel1GeoKey;
    1377           2 :         panProjParamId[5] = ProjFalseEastingGeoKey;
    1378           2 :         panProjParamId[6] = ProjFalseNorthingGeoKey;
    1379             : 
    1380           2 :         panEPSGCodes[0] = EPSGNatOriginLat;
    1381           2 :         panEPSGCodes[1] = EPSGNatOriginLong;
    1382           2 :         panEPSGCodes[2] = EPSGStdParallel1Lat;
    1383           2 :         panEPSGCodes[5] = EPSGFalseEasting;
    1384           2 :         panEPSGCodes[6] = EPSGFalseNorthing;
    1385           2 :         return TRUE;
    1386             : 
    1387          10 :       default:
    1388          10 :         return FALSE;
    1389             :     }
    1390             : }
    1391             : 
    1392             : /************************************************************************/
    1393             : /*                         GTIFGetProjTRFInfo()                         */
    1394             : /*                                                                      */
    1395             : /*      Transform a PROJECTION_TRF_CODE into a projection method,       */
    1396             : /*      and a set of parameters.  The parameters identify will          */
    1397             : /*      depend on the returned method, but they will all have been      */
    1398             : /*      normalized into degrees and meters.                             */
    1399             : /************************************************************************/
    1400             : 
    1401        3219 : int GTIFGetProjTRFInfoEx( void* ctxIn,
    1402             :                           int nProjTRFCode,
    1403             :                           char **ppszProjTRFName,
    1404             :                           short * pnProjMethod,
    1405             :                           double * padfProjParams )
    1406             : 
    1407             : {
    1408        3219 :     if ((nProjTRFCode >= Proj_UTM_zone_1N && nProjTRFCode <= Proj_UTM_zone_60N) ||
    1409          50 :         (nProjTRFCode >= Proj_UTM_zone_1S && nProjTRFCode <= Proj_UTM_zone_60S))
    1410             :     {
    1411             :         int bNorth;
    1412             :         int nZone;
    1413        3063 :         if (nProjTRFCode <= Proj_UTM_zone_60N)
    1414             :         {
    1415        3060 :             bNorth = TRUE;
    1416        3060 :             nZone = nProjTRFCode - Proj_UTM_zone_1N + 1;
    1417             :         }
    1418             :         else
    1419             :         {
    1420           3 :             bNorth = FALSE;
    1421           3 :             nZone = nProjTRFCode - Proj_UTM_zone_1S + 1;
    1422             :         }
    1423             : 
    1424        3063 :         if (ppszProjTRFName)
    1425             :         {
    1426             :             char szProjTRFName[64];
    1427           0 :             snprintf(szProjTRFName, sizeof(szProjTRFName), "UTM zone %d%c",
    1428             :                      nZone, (bNorth) ? 'N' : 'S');
    1429           0 :             *ppszProjTRFName = CPLStrdup(szProjTRFName);
    1430             :         }
    1431             : 
    1432        3063 :         if (pnProjMethod)
    1433        3063 :             *pnProjMethod = 9807;
    1434             : 
    1435        3063 :         if (padfProjParams)
    1436             :         {
    1437        3063 :             padfProjParams[0] = 0;
    1438        3063 :             padfProjParams[1] = -183 + 6 * nZone;
    1439        3063 :             padfProjParams[2] = 0;
    1440        3063 :             padfProjParams[3] = 0;
    1441        3063 :             padfProjParams[4] = 0.9996;
    1442        3063 :             padfProjParams[5] = 500000;
    1443        3063 :             padfProjParams[6] = (bNorth) ? 0 : 10000000;
    1444             :         }
    1445             : 
    1446        3063 :         return TRUE;
    1447             :     }
    1448             : 
    1449         156 :     if( nProjTRFCode == KvUserDefined )
    1450           0 :         return FALSE;
    1451             : 
    1452             :     {
    1453             :         char    szCode[12];
    1454         156 :         snprintf(szCode, sizeof(szCode), "%d", nProjTRFCode);
    1455         156 :         PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
    1456         156 :         PJ *transf = proj_create_from_database(
    1457             :             ctx, "EPSG", szCode, PJ_CATEGORY_COORDINATE_OPERATION, 0, NULL);
    1458         156 :         if( !transf )
    1459             :         {
    1460           0 :             return FALSE;
    1461             :         }
    1462             : 
    1463         156 :         if( proj_get_type(transf) != PJ_TYPE_CONVERSION )
    1464             :         {
    1465           0 :             proj_destroy(transf);
    1466           0 :             return FALSE;
    1467             :         }
    1468             : 
    1469             :         /* Get the projection method code */
    1470         156 :         const char* pszMethodCode = NULL;
    1471         156 :         proj_coordoperation_get_method_info(ctx, transf,
    1472             :                                             NULL, /* method name */
    1473             :                                             NULL, /* method auth name (should be EPSG) */
    1474             :                                             &pszMethodCode);
    1475         156 :         assert( pszMethodCode );
    1476         156 :         const int nProjMethod = atoi(pszMethodCode);
    1477             : 
    1478             : /* -------------------------------------------------------------------- */
    1479             : /*      Initialize a definition of what EPSG codes need to be loaded    */
    1480             : /*      into what fields in adfProjParams.                               */
    1481             : /* -------------------------------------------------------------------- */
    1482         156 :         const int nCTProjMethod = EPSGProjMethodToCTProjMethod( nProjMethod, TRUE );
    1483             :         int anEPSGCodes[7];
    1484         156 :         SetGTParamIds( nCTProjMethod, nProjMethod, NULL, anEPSGCodes );
    1485             : 
    1486             : 
    1487             : /* -------------------------------------------------------------------- */
    1488             : /*      Get the parameters for this projection.                         */
    1489             : /* -------------------------------------------------------------------- */
    1490             : 
    1491             :         double  adfProjParams[7];
    1492             : 
    1493        1248 :         for( int i = 0; i < 7; i++ )
    1494             :         {
    1495        1092 :             int nEPSGCode = anEPSGCodes[i];
    1496             : 
    1497             :             /* Establish default */
    1498        1092 :             if( nEPSGCode == EPSGAngleRectifiedToSkewedGrid )
    1499           3 :                 adfProjParams[i] = 90.0;
    1500        1089 :             else if( nEPSGCode == EPSGNatOriginScaleFactor
    1501         954 :                     || nEPSGCode == EPSGInitialLineScaleFactor
    1502         951 :                     || nEPSGCode == EPSGPseudoStdParallelScaleFactor )
    1503         138 :                 adfProjParams[i] = 1.0;
    1504             :             else
    1505         951 :                 adfProjParams[i] = 0.0;
    1506             : 
    1507             :             /* If there is no parameter, skip */
    1508        1092 :             if( nEPSGCode == 0 )
    1509         395 :                 continue;
    1510             : 
    1511         762 :             const int nParamCount = proj_coordoperation_get_param_count(ctx, transf);
    1512             : 
    1513             :             /* Find the matching parameter */
    1514         762 :             const char *pszUOMCategory = NULL;
    1515         762 :             double  dfValue = 0.0;
    1516         762 :             double  dfUnitConvFactor = 0.0;
    1517             :             int iEPSG;  /* Used after for */
    1518        2331 :             for( iEPSG = 0; iEPSG < nParamCount; iEPSG++ )
    1519             :             {
    1520        2260 :                 const char* pszParamCode = NULL;
    1521        2260 :                 proj_coordoperation_get_param(
    1522             :                     ctx, transf, iEPSG,
    1523             :                     NULL, /* name */
    1524             :                     NULL, /* auth name */
    1525             :                     &pszParamCode,
    1526             :                     &dfValue,
    1527             :                     NULL, /* value (string) */
    1528             :                     &dfUnitConvFactor, /* unit conv factor */
    1529             :                     NULL, /* unit name */
    1530             :                     NULL, /* unit auth name */
    1531             :                     NULL, /* unit code */
    1532             :                     &pszUOMCategory /* unit category */);
    1533        2260 :                 assert(pszParamCode);
    1534        2260 :                 if( atoi(pszParamCode) == nEPSGCode )
    1535             :                 {
    1536         691 :                     break;
    1537             :                 }
    1538             :             }
    1539             : 
    1540             :             /* not found, accept the default */
    1541         762 :             if( iEPSG == nParamCount )
    1542             :             {
    1543             :                 /* for CT_ObliqueMercator try alternate parameter codes first */
    1544             :                 /* because EPSG proj method 9812 uses EPSGFalseXXXXX, but 9815 uses EPSGProjCenterXXXXX */
    1545          71 :                 if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterEasting )
    1546           1 :                     nEPSGCode = EPSGFalseEasting;
    1547          70 :                 else if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterNorthing )
    1548           1 :                     nEPSGCode = EPSGFalseNorthing;
    1549             :                 /* for CT_PolarStereographic try alternate parameter codes first */
    1550             :                 /* because EPSG proj method 9829 uses EPSGLatOfStdParallel instead of EPSGNatOriginLat */
    1551             :                 /* and EPSGOriginLong instead of EPSGNatOriginLong */
    1552          69 :                 else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLat )
    1553           2 :                     nEPSGCode = EPSGLatOfStdParallel;
    1554          67 :                 else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLong )
    1555           2 :                     nEPSGCode = EPSGOriginLong;
    1556             :                 else
    1557          65 :                     continue;
    1558             : 
    1559          19 :                 for( iEPSG = 0; iEPSG < nParamCount; iEPSG++ )
    1560             :                 {
    1561          19 :                     const char* pszParamCode = NULL;
    1562          19 :                     proj_coordoperation_get_param(
    1563             :                         ctx, transf, iEPSG,
    1564             :                         NULL, /* name */
    1565             :                         NULL, /* auth name */
    1566             :                         &pszParamCode,
    1567             :                         &dfValue,
    1568             :                         NULL, /* value (string) */
    1569             :                         &dfUnitConvFactor, /* unit conv factor */
    1570             :                         NULL, /* unit name */
    1571             :                         NULL, /* unit auth name */
    1572             :                         NULL, /* unit code */
    1573             :                         &pszUOMCategory /* unit category */);
    1574          19 :                     assert(pszParamCode);
    1575          19 :                     if( atoi(pszParamCode) == nEPSGCode )
    1576             :                     {
    1577           6 :                         break;
    1578             :                     }
    1579             :                 }
    1580             : 
    1581           6 :                 if( iEPSG == nParamCount )
    1582           0 :                     continue;
    1583             :             }
    1584             : 
    1585         697 :             assert(pszUOMCategory);
    1586             : 
    1587         697 :             adfProjParams[i] = dfValue * dfUnitConvFactor;
    1588         697 :             if( strcmp(pszUOMCategory, "angular") == 0.0 )
    1589             :             {
    1590             :                 /* Convert from radians to degrees */
    1591         318 :                 adfProjParams[i] *= 180 / M_PI;
    1592             :             }
    1593             :         }
    1594             : 
    1595             : /* -------------------------------------------------------------------- */
    1596             : /*      Get the name, if requested.                                     */
    1597             : /* -------------------------------------------------------------------- */
    1598         156 :         if( ppszProjTRFName != NULL )
    1599             :         {
    1600           0 :             const char* pszName = proj_get_name(transf);
    1601           0 :             if( !pszName )
    1602             :             {
    1603             :                 // shouldn't happen
    1604           0 :                 proj_destroy(transf);
    1605           0 :                 return FALSE;
    1606             :             }
    1607           0 :             *ppszProjTRFName = CPLStrdup(pszName);
    1608             :         }
    1609             : 
    1610             : /* -------------------------------------------------------------------- */
    1611             : /*      Transfer requested data into passed variables.                  */
    1612             : /* -------------------------------------------------------------------- */
    1613         156 :         if( pnProjMethod != NULL )
    1614         156 :             *pnProjMethod = (short) nProjMethod;
    1615             : 
    1616         156 :         if( padfProjParams != NULL )
    1617             :         {
    1618        1248 :             for( int i = 0; i < 7; i++ )
    1619        1092 :                 padfProjParams[i] = adfProjParams[i];
    1620             :         }
    1621             : 
    1622         156 :         proj_destroy(transf);
    1623             : 
    1624         156 :         return TRUE;
    1625             :     }
    1626             : }
    1627             : 
    1628           0 : int GTIFGetProjTRFInfo( /* Conversion code */
    1629             :                         int nProjTRFCode,
    1630             :                         char **ppszProjTRFName,
    1631             :                         short * pnProjMethod,
    1632             :                         double * padfProjParams )
    1633             : {
    1634           0 :     PJ_CONTEXT* ctx = proj_context_create();
    1635           0 :     const int ret = GTIFGetProjTRFInfoEx(
    1636             :         ctx, nProjTRFCode, ppszProjTRFName, pnProjMethod, padfProjParams);
    1637           0 :     proj_context_destroy(ctx);
    1638           0 :     return ret;
    1639             : }
    1640             : 
    1641             : /************************************************************************/
    1642             : /*                         GTIFFetchProjParms()                         */
    1643             : /*                                                                      */
    1644             : /*      Fetch the projection parameters for a particular projection     */
    1645             : /*      from a GeoTIFF file, and fill the GTIFDefn structure out        */
    1646             : /*      with them.                                                      */
    1647             : /************************************************************************/
    1648             : 
    1649          86 : static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn )
    1650             : {
    1651             : 
    1652             : /* -------------------------------------------------------------------- */
    1653             : /*      Get the false easting, and northing if available.               */
    1654             : /* -------------------------------------------------------------------- */
    1655          86 :     double dfFalseEasting = 0.0;
    1656          86 :     if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFalseEasting, 0, 1)
    1657          15 :         && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterEastingGeoKey,
    1658             :                        &dfFalseEasting, 0, 1)
    1659          15 :         && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey,
    1660             :                        &dfFalseEasting, 0, 1) )
    1661           0 :         dfFalseEasting = 0.0;
    1662             : 
    1663          86 :     double dfFalseNorthing = 0.0;
    1664          86 :     if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFalseNorthing,0,1)
    1665          15 :         && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterNorthingGeoKey,
    1666             :                        &dfFalseNorthing, 0, 1)
    1667          15 :         && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey,
    1668             :                        &dfFalseNorthing, 0, 1) )
    1669           0 :         dfFalseNorthing = 0.0;
    1670             : 
    1671          86 :     double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0;
    1672          86 :     double dfNatOriginScale = 1.0;
    1673          86 :     double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0;
    1674             : 
    1675          86 :     switch( psDefn->CTProjection )
    1676             :     {
    1677             : /* -------------------------------------------------------------------- */
    1678           0 :       case CT_Stereographic:
    1679             : /* -------------------------------------------------------------------- */
    1680           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1681             :                        &dfNatOriginLong, 0, 1 ) == 0
    1682           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1683             :                           &dfNatOriginLong, 0, 1 ) == 0
    1684           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1685             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1686           0 :             dfNatOriginLong = 0.0;
    1687             : 
    1688           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1689             :                        &dfNatOriginLat, 0, 1 ) == 0
    1690           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1691             :                           &dfNatOriginLat, 0, 1 ) == 0
    1692           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1693             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1694           0 :             dfNatOriginLat = 0.0;
    1695             : 
    1696           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    1697             :                        &dfNatOriginScale, 0, 1 ) == 0 )
    1698           0 :             dfNatOriginScale = 1.0;
    1699             : 
    1700             :         /* notdef: should transform to decimal degrees at this point */
    1701             : 
    1702           0 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1703           0 :         psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
    1704           0 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1705           0 :         psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
    1706           0 :         psDefn->ProjParm[4] = dfNatOriginScale;
    1707           0 :         psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
    1708           0 :         psDefn->ProjParm[5] = dfFalseEasting;
    1709           0 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1710           0 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1711           0 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1712             : 
    1713           0 :         psDefn->nParms = 7;
    1714           0 :         break;
    1715             : 
    1716             : /* -------------------------------------------------------------------- */
    1717          11 :       case CT_Mercator:
    1718             : /* -------------------------------------------------------------------- */
    1719          11 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1720             :                        &dfNatOriginLong, 0, 1 ) == 0
    1721           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1722             :                           &dfNatOriginLong, 0, 1 ) == 0
    1723           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1724             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1725           0 :             dfNatOriginLong = 0.0;
    1726             : 
    1727          11 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1728             :                        &dfNatOriginLat, 0, 1 ) == 0
    1729           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1730             :                           &dfNatOriginLat, 0, 1 ) == 0
    1731           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1732             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1733           0 :             dfNatOriginLat = 0.0;
    1734             : 
    1735             : 
    1736          11 :         const int bHaveSP1 = GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
    1737             :                               &dfStdParallel1, 0, 1 );
    1738             : 
    1739          11 :         int bHaveNOS = GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    1740             :                               &dfNatOriginScale, 0, 1 );
    1741             : 
    1742             :         /* Default scale only if dfStdParallel1 isn't defined either */
    1743          11 :         if( !bHaveNOS && !bHaveSP1)
    1744             :         {
    1745           0 :             bHaveNOS = TRUE;
    1746           0 :             dfNatOriginScale = 1.0;
    1747             :         }
    1748             : 
    1749             :         /* notdef: should transform to decimal degrees at this point */
    1750             : 
    1751          11 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1752          11 :         psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
    1753          11 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1754          11 :         psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
    1755          11 :         if( bHaveSP1 )
    1756             :         {
    1757           3 :             psDefn->ProjParm[2] = dfStdParallel1;
    1758           3 :             psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
    1759             :         }
    1760          11 :         if( bHaveNOS )
    1761             :         {
    1762          10 :             psDefn->ProjParm[4] = dfNatOriginScale;
    1763          10 :             psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
    1764             :         }
    1765          11 :         psDefn->ProjParm[5] = dfFalseEasting;
    1766          11 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1767          11 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1768          11 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1769             : 
    1770          11 :         psDefn->nParms = 7;
    1771          11 :         break;
    1772             : 
    1773             : /* -------------------------------------------------------------------- */
    1774          10 :       case CT_LambertConfConic_1SP:
    1775             :       case CT_ObliqueStereographic:
    1776             :       case CT_TransverseMercator:
    1777             :       case CT_TransvMercator_SouthOriented:
    1778             : /* -------------------------------------------------------------------- */
    1779          10 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1780             :                        &dfNatOriginLong, 0, 1 ) == 0
    1781           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1782             :                           &dfNatOriginLong, 0, 1 ) == 0
    1783           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1784             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1785           0 :             dfNatOriginLong = 0.0;
    1786             : 
    1787          10 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1788             :                        &dfNatOriginLat, 0, 1 ) == 0
    1789           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1790             :                           &dfNatOriginLat, 0, 1 ) == 0
    1791           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1792             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1793           0 :             dfNatOriginLat = 0.0;
    1794             : 
    1795          10 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    1796             :                        &dfNatOriginScale, 0, 1 ) == 0
    1797             :             /* See https://github.com/OSGeo/gdal/files/1665718/lasinfo.txt */
    1798           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
    1799             :                        &dfNatOriginScale, 0, 1 ) == 0 )
    1800           0 :             dfNatOriginScale = 1.0;
    1801             : 
    1802             :         /* notdef: should transform to decimal degrees at this point */
    1803             : 
    1804          10 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1805          10 :         psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
    1806          10 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1807          10 :         psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
    1808          10 :         psDefn->ProjParm[4] = dfNatOriginScale;
    1809          10 :         psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
    1810          10 :         psDefn->ProjParm[5] = dfFalseEasting;
    1811          10 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1812          10 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1813          10 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1814             : 
    1815          10 :         psDefn->nParms = 7;
    1816          10 :         break;
    1817             : 
    1818             : /* -------------------------------------------------------------------- */
    1819           3 :       case CT_ObliqueMercator: /* hotine */
    1820             :       case CT_HotineObliqueMercatorAzimuthCenter:
    1821             : /* -------------------------------------------------------------------- */
    1822           3 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1823             :                        &dfNatOriginLong, 0, 1 ) == 0
    1824           3 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1825             :                           &dfNatOriginLong, 0, 1 ) == 0
    1826           3 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1827             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1828           0 :             dfNatOriginLong = 0.0;
    1829             : 
    1830           3 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1831             :                        &dfNatOriginLat, 0, 1 ) == 0
    1832           3 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1833             :                           &dfNatOriginLat, 0, 1 ) == 0
    1834           3 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1835             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1836           0 :             dfNatOriginLat = 0.0;
    1837             : 
    1838           3 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjAzimuthAngleGeoKey,
    1839             :                        &dfAzimuth, 0, 1 ) == 0 )
    1840           0 :             dfAzimuth = 0.0;
    1841             : 
    1842           3 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjRectifiedGridAngleGeoKey,
    1843             :                        &dfRectGridAngle, 0, 1 ) == 0 )
    1844           0 :             dfRectGridAngle = 90.0;
    1845             : 
    1846           3 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    1847             :                        &dfNatOriginScale, 0, 1 ) == 0
    1848           3 :             && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
    1849             :                           &dfNatOriginScale, 0, 1 ) == 0 )
    1850           0 :             dfNatOriginScale = 1.0;
    1851             : 
    1852             :         /* notdef: should transform to decimal degrees at this point */
    1853             : 
    1854           3 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1855           3 :         psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
    1856           3 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1857           3 :         psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
    1858           3 :         psDefn->ProjParm[2] = dfAzimuth;
    1859           3 :         psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
    1860           3 :         psDefn->ProjParm[3] = dfRectGridAngle;
    1861           3 :         psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey;
    1862           3 :         psDefn->ProjParm[4] = dfNatOriginScale;
    1863           3 :         psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
    1864           3 :         psDefn->ProjParm[5] = dfFalseEasting;
    1865           3 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1866           3 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1867           3 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1868             : 
    1869           3 :         psDefn->nParms = 7;
    1870           3 :         break;
    1871             : 
    1872             : /* -------------------------------------------------------------------- */
    1873           0 :       case CT_ObliqueMercator_Laborde:
    1874             : /* -------------------------------------------------------------------- */
    1875           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1876             :                        &dfNatOriginLong, 0, 1 ) == 0
    1877           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1878             :                           &dfNatOriginLong, 0, 1 ) == 0
    1879           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1880             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1881           0 :             dfNatOriginLong = 0.0;
    1882             : 
    1883           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1884             :                        &dfNatOriginLat, 0, 1 ) == 0
    1885           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1886             :                           &dfNatOriginLat, 0, 1 ) == 0
    1887           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1888             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1889           0 :             dfNatOriginLat = 0.0;
    1890             : 
    1891           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjAzimuthAngleGeoKey,
    1892             :                        &dfAzimuth, 0, 1 ) == 0 )
    1893           0 :             dfAzimuth = 0.0;
    1894             : 
    1895           0 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    1896             :                        &dfNatOriginScale, 0, 1 ) == 0
    1897           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
    1898             :                           &dfNatOriginScale, 0, 1 ) == 0 )
    1899           0 :             dfNatOriginScale = 1.0;
    1900             : 
    1901             :         /* notdef: should transform to decimal degrees at this point */
    1902             : 
    1903           0 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1904           0 :         psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
    1905           0 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1906           0 :         psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
    1907           0 :         psDefn->ProjParm[2] = dfAzimuth;
    1908           0 :         psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
    1909           0 :         psDefn->ProjParm[4] = dfNatOriginScale;
    1910           0 :         psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
    1911           0 :         psDefn->ProjParm[5] = dfFalseEasting;
    1912           0 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1913           0 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1914           0 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1915             : 
    1916           0 :         psDefn->nParms = 7;
    1917           0 :         break;
    1918             : 
    1919             : /* -------------------------------------------------------------------- */
    1920           2 :       case CT_CassiniSoldner:
    1921             :       case CT_Polyconic:
    1922             : /* -------------------------------------------------------------------- */
    1923           2 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1924             :                        &dfNatOriginLong, 0, 1 ) == 0
    1925           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1926             :                           &dfNatOriginLong, 0, 1 ) == 0
    1927           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1928             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1929           0 :             dfNatOriginLong = 0.0;
    1930             : 
    1931           2 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1932             :                        &dfNatOriginLat, 0, 1 ) == 0
    1933           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1934             :                           &dfNatOriginLat, 0, 1 ) == 0
    1935           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1936             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1937           0 :             dfNatOriginLat = 0.0;
    1938             : 
    1939           2 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    1940             :                        &dfNatOriginScale, 0, 1 ) == 0
    1941           1 :             && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
    1942             :                           &dfNatOriginScale, 0, 1 ) == 0 )
    1943           1 :             dfNatOriginScale = 1.0;
    1944             : 
    1945             :         /* notdef: should transform to decimal degrees at this point */
    1946             : 
    1947           2 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1948           2 :         psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
    1949           2 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1950           2 :         psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
    1951           2 :         psDefn->ProjParm[4] = dfNatOriginScale;
    1952           2 :         psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
    1953           2 :         psDefn->ProjParm[5] = dfFalseEasting;
    1954           2 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1955           2 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1956           2 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1957             : 
    1958           2 :         psDefn->nParms = 7;
    1959           2 :         break;
    1960             : 
    1961             : /* -------------------------------------------------------------------- */
    1962          20 :       case CT_AzimuthalEquidistant:
    1963             :       case CT_MillerCylindrical:
    1964             :       case CT_Gnomonic:
    1965             :       case CT_LambertAzimEqualArea:
    1966             :       case CT_Orthographic:
    1967             :       case CT_NewZealandMapGrid:
    1968             : /* -------------------------------------------------------------------- */
    1969          20 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    1970             :                        &dfNatOriginLong, 0, 1 ) == 0
    1971          19 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    1972             :                           &dfNatOriginLong, 0, 1 ) == 0
    1973          19 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    1974             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    1975           0 :             dfNatOriginLong = 0.0;
    1976             : 
    1977          20 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    1978             :                        &dfNatOriginLat, 0, 1 ) == 0
    1979          19 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    1980             :                           &dfNatOriginLat, 0, 1 ) == 0
    1981          19 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    1982             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    1983           0 :             dfNatOriginLat = 0.0;
    1984             : 
    1985             :         /* notdef: should transform to decimal degrees at this point */
    1986             : 
    1987          20 :         psDefn->ProjParm[0] = dfNatOriginLat;
    1988          20 :         psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
    1989          20 :         psDefn->ProjParm[1] = dfNatOriginLong;
    1990          20 :         psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
    1991          20 :         psDefn->ProjParm[5] = dfFalseEasting;
    1992          20 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    1993          20 :         psDefn->ProjParm[6] = dfFalseNorthing;
    1994          20 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    1995             : 
    1996          20 :         psDefn->nParms = 7;
    1997          20 :         break;
    1998             : 
    1999             : /* -------------------------------------------------------------------- */
    2000           5 :       case CT_Equirectangular:
    2001             : /* -------------------------------------------------------------------- */
    2002           5 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    2003             :                        &dfNatOriginLong, 0, 1 ) == 0
    2004           5 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    2005             :                           &dfNatOriginLong, 0, 1 ) == 0
    2006           5 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    2007             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    2008           0 :             dfNatOriginLong = 0.0;
    2009             : 
    2010           5 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    2011             :                        &dfNatOriginLat, 0, 1 ) == 0
    2012           5 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    2013             :                           &dfNatOriginLat, 0, 1 ) == 0
    2014           5 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    2015             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    2016           0 :             dfNatOriginLat = 0.0;
    2017             : 
    2018           5 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
    2019             :                        &dfStdParallel1, 0, 1 ) == 0 )
    2020           0 :             dfStdParallel1 = 0.0;
    2021             : 
    2022             :         /* notdef: should transform to decimal degrees at this point */
    2023             : 
    2024           5 :         psDefn->ProjParm[0] = dfNatOriginLat;
    2025           5 :         psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
    2026           5 :         psDefn->ProjParm[1] = dfNatOriginLong;
    2027           5 :         psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
    2028           5 :         psDefn->ProjParm[2] = dfStdParallel1;
    2029           5 :         psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
    2030           5 :         psDefn->ProjParm[5] = dfFalseEasting;
    2031           5 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2032           5 :         psDefn->ProjParm[6] = dfFalseNorthing;
    2033           5 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2034             : 
    2035           5 :         psDefn->nParms = 7;
    2036           5 :         break;
    2037             : 
    2038             : /* -------------------------------------------------------------------- */
    2039           7 :       case CT_Robinson:
    2040             :       case CT_Sinusoidal:
    2041             :       case CT_VanDerGrinten:
    2042             : /* -------------------------------------------------------------------- */
    2043           7 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    2044             :                        &dfNatOriginLong, 0, 1 ) == 0
    2045           7 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    2046             :                           &dfNatOriginLong, 0, 1 ) == 0
    2047           7 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    2048             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    2049           0 :             dfNatOriginLong = 0.0;
    2050             : 
    2051             :         /* notdef: should transform to decimal degrees at this point */
    2052             : 
    2053           7 :         psDefn->ProjParm[1] = dfNatOriginLong;
    2054           7 :         psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
    2055           7 :         psDefn->ProjParm[5] = dfFalseEasting;
    2056           7 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2057           7 :         psDefn->ProjParm[6] = dfFalseNorthing;
    2058           7 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2059             : 
    2060           7 :         psDefn->nParms = 7;
    2061           7 :         break;
    2062             : 
    2063             : /* -------------------------------------------------------------------- */
    2064           6 :       case CT_PolarStereographic:
    2065             : /* -------------------------------------------------------------------- */
    2066           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStraightVertPoleLongGeoKey,
    2067             :                        &dfNatOriginLong, 0, 1 ) == 0
    2068           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    2069             :                           &dfNatOriginLong, 0, 1 ) == 0
    2070           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    2071             :                           &dfNatOriginLong, 0, 1 ) == 0
    2072           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    2073             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    2074           0 :             dfNatOriginLong = 0.0;
    2075             : 
    2076           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    2077             :                        &dfNatOriginLat, 0, 1 ) == 0
    2078           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    2079             :                           &dfNatOriginLat, 0, 1 ) == 0
    2080           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    2081             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    2082           0 :             dfNatOriginLat = 0.0;
    2083             : 
    2084           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
    2085             :                        &dfNatOriginScale, 0, 1 ) == 0
    2086           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
    2087             :                           &dfNatOriginScale, 0, 1 ) == 0 )
    2088           0 :             dfNatOriginScale = 1.0;
    2089             : 
    2090             :         /* notdef: should transform to decimal degrees at this point */
    2091             : 
    2092           6 :         psDefn->ProjParm[0] = dfNatOriginLat;
    2093           6 :         psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;;
    2094           6 :         psDefn->ProjParm[1] = dfNatOriginLong;
    2095           6 :         psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey;
    2096           6 :         psDefn->ProjParm[4] = dfNatOriginScale;
    2097           6 :         psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
    2098           6 :         psDefn->ProjParm[5] = dfFalseEasting;
    2099           6 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2100           6 :         psDefn->ProjParm[6] = dfFalseNorthing;
    2101           6 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2102             : 
    2103           6 :         psDefn->nParms = 7;
    2104           6 :         break;
    2105             : 
    2106             : /* -------------------------------------------------------------------- */
    2107          15 :       case CT_LambertConfConic_2SP:
    2108             : /* -------------------------------------------------------------------- */
    2109          15 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
    2110             :                        &dfStdParallel1, 0, 1 ) == 0 )
    2111           0 :             dfStdParallel1 = 0.0;
    2112             : 
    2113          15 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey,
    2114             :                        &dfStdParallel2, 0, 1 ) == 0 )
    2115           0 :             dfStdParallel1 = 0.0;
    2116             : 
    2117          15 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    2118             :                        &dfNatOriginLong, 0, 1 ) == 0
    2119          15 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    2120             :                           &dfNatOriginLong, 0, 1 ) == 0
    2121           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    2122             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    2123           0 :             dfNatOriginLong = 0.0;
    2124             : 
    2125          15 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    2126             :                        &dfNatOriginLat, 0, 1 ) == 0
    2127          15 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    2128             :                           &dfNatOriginLat, 0, 1 ) == 0
    2129           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    2130             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    2131           0 :             dfNatOriginLat = 0.0;
    2132             : 
    2133             :         /* notdef: should transform to decimal degrees at this point */
    2134             : 
    2135          15 :         psDefn->ProjParm[0] = dfNatOriginLat;
    2136          15 :         psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey;
    2137          15 :         psDefn->ProjParm[1] = dfNatOriginLong;
    2138          15 :         psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey;
    2139          15 :         psDefn->ProjParm[2] = dfStdParallel1;
    2140          15 :         psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
    2141          15 :         psDefn->ProjParm[3] = dfStdParallel2;
    2142          15 :         psDefn->ProjParmId[3] = ProjStdParallel2GeoKey;
    2143          15 :         psDefn->ProjParm[5] = dfFalseEasting;
    2144          15 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2145          15 :         psDefn->ProjParm[6] = dfFalseNorthing;
    2146          15 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2147             : 
    2148          15 :         psDefn->nParms = 7;
    2149          15 :         break;
    2150             : 
    2151             : /* -------------------------------------------------------------------- */
    2152           6 :       case CT_AlbersEqualArea:
    2153             :       case CT_EquidistantConic:
    2154             : /* -------------------------------------------------------------------- */
    2155           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
    2156             :                        &dfStdParallel1, 0, 1 ) == 0 )
    2157           0 :             dfStdParallel1 = 0.0;
    2158             : 
    2159           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey,
    2160             :                        &dfStdParallel2, 0, 1 ) == 0 )
    2161           0 :             dfStdParallel2 = 0.0;
    2162             : 
    2163           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    2164             :                        &dfNatOriginLong, 0, 1 ) == 0
    2165           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    2166             :                           &dfNatOriginLong, 0, 1 ) == 0
    2167           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    2168             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    2169           0 :             dfNatOriginLong = 0.0;
    2170             : 
    2171           6 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
    2172             :                        &dfNatOriginLat, 0, 1 ) == 0
    2173           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
    2174             :                           &dfNatOriginLat, 0, 1 ) == 0
    2175           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
    2176             :                           &dfNatOriginLat, 0, 1 ) == 0 )
    2177           0 :             dfNatOriginLat = 0.0;
    2178             : 
    2179             :         /* notdef: should transform to decimal degrees at this point */
    2180             : 
    2181           6 :         psDefn->ProjParm[0] = dfStdParallel1;
    2182           6 :         psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
    2183           6 :         psDefn->ProjParm[1] = dfStdParallel2;
    2184           6 :         psDefn->ProjParmId[1] = ProjStdParallel2GeoKey;
    2185           6 :         psDefn->ProjParm[2] = dfNatOriginLat;
    2186           6 :         psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey;
    2187           6 :         psDefn->ProjParm[3] = dfNatOriginLong;
    2188           6 :         psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey;
    2189           6 :         psDefn->ProjParm[5] = dfFalseEasting;
    2190           6 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2191           6 :         psDefn->ProjParm[6] = dfFalseNorthing;
    2192           6 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2193             : 
    2194           6 :         psDefn->nParms = 7;
    2195           6 :         break;
    2196             : 
    2197             : /* -------------------------------------------------------------------- */
    2198           1 :       case CT_CylindricalEqualArea:
    2199             : /* -------------------------------------------------------------------- */
    2200           1 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
    2201             :                        &dfStdParallel1, 0, 1 ) == 0 )
    2202           0 :             dfStdParallel1 = 0.0;
    2203             : 
    2204           1 :         if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
    2205             :                        &dfNatOriginLong, 0, 1 ) == 0
    2206           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
    2207             :                           &dfNatOriginLong, 0, 1 ) == 0
    2208           0 :             && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
    2209             :                           &dfNatOriginLong, 0, 1 ) == 0 )
    2210           0 :             dfNatOriginLong = 0.0;
    2211             : 
    2212             :         /* notdef: should transform to decimal degrees at this point */
    2213             : 
    2214           1 :         psDefn->ProjParm[0] = dfStdParallel1;
    2215           1 :         psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
    2216           1 :         psDefn->ProjParm[1] = dfNatOriginLong;
    2217           1 :         psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
    2218           1 :         psDefn->ProjParm[5] = dfFalseEasting;
    2219           1 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2220           1 :         psDefn->ProjParm[6] = dfFalseNorthing;
    2221           1 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2222             : 
    2223           1 :         psDefn->nParms = 7;
    2224           1 :         break;
    2225             :     }
    2226             : 
    2227         688 :     for( int iParam = 0; iParam < psDefn->nParms; iParam++ )
    2228             :     {
    2229         602 :         switch( psDefn->ProjParmId[iParam] )
    2230             :         {
    2231             : 
    2232             : /* -------------------------------------------------------------------- */
    2233             : /*      Normalize any linear parameters into meters.  In GeoTIFF        */
    2234             : /*      the linear projection parameter tags are normally in the        */
    2235             : /*      units of the coordinate system described.                       */
    2236             : /* -------------------------------------------------------------------- */
    2237         172 :           case ProjFalseEastingGeoKey:
    2238             :           case ProjFalseNorthingGeoKey:
    2239             :           case ProjFalseOriginEastingGeoKey:
    2240             :           case ProjFalseOriginNorthingGeoKey:
    2241             :           case ProjCenterEastingGeoKey:
    2242             :           case ProjCenterNorthingGeoKey:
    2243         172 :             if( psDefn->UOMLengthInMeters != 0
    2244         172 :                 && psDefn->UOMLengthInMeters != 1.0 )
    2245             :             {
    2246          32 :                 psDefn->ProjParm[iParam] *= psDefn->UOMLengthInMeters;
    2247             :             }
    2248         172 :             break;
    2249             : 
    2250             : /* -------------------------------------------------------------------- */
    2251             : /*      Normalize any angular parameters into degrees.  In GeoTIFF      */
    2252             : /*      the angular projection parameter tags are normally in the       */
    2253             : /*      units of GeogAngularUnit. Note: this conversion is only done    */
    2254             : /*      since libgeotiff 1.7.4                                          */
    2255             : /* -------------------------------------------------------------------- */
    2256             : 
    2257         218 :           case ProjStdParallel1GeoKey:
    2258             :           case ProjStdParallel2GeoKey:
    2259             :           case ProjNatOriginLongGeoKey:
    2260             :           case ProjNatOriginLatGeoKey:
    2261             :           case ProjFalseOriginLongGeoKey:
    2262             :           case ProjFalseOriginLatGeoKey:
    2263             :           case ProjCenterLongGeoKey:
    2264             :           case ProjCenterLatGeoKey:
    2265             :           case ProjStraightVertPoleLongGeoKey:
    2266             :           case ProjRectifiedGridAngleGeoKey:
    2267         218 :             if( psDefn->UOMAngleInDegrees != 0
    2268         218 :                 && psDefn->UOMAngleInDegrees != 1.0 )
    2269             :             {
    2270           6 :                 psDefn->ProjParm[iParam] *= psDefn->UOMAngleInDegrees;
    2271             :             }
    2272         218 :             break;
    2273             : 
    2274         212 :           default:
    2275         212 :             break;
    2276             :         }
    2277             :     }
    2278          86 : }
    2279             : 
    2280             : /************************************************************************/
    2281             : /*                            GTIFGetDefn()                             */
    2282             : /************************************************************************/
    2283             : 
    2284             : /**
    2285             : @param psGTIF GeoTIFF information handle as returned by GTIFNew.
    2286             : @param psDefn Pointer to an existing GTIFDefn structure allocated by GTIFAllocDefn().
    2287             : 
    2288             : @return TRUE if the function has been successful, otherwise FALSE.
    2289             : 
    2290             : This function reads the coordinate system definition from a GeoTIFF file,
    2291             : and <i>normalizes</i> it into a set of component information using
    2292             : definitions from the EPSG database as provided by the PROJ library.
    2293             : This function is intended to simplify correct support for
    2294             : reading files with defined PCS (Projected Coordinate System) codes that
    2295             : wouldn't otherwise be directly known by application software by reducing
    2296             : it to the underlying projection method, parameters, datum, ellipsoid,
    2297             : prime meridian and units.<p>
    2298             : 
    2299             : The application should pass a pointer to an existing uninitialized
    2300             : GTIFDefn structure, and GTIFGetDefn() will fill it in.  The function
    2301             : currently always returns TRUE but in the future will return FALSE if
    2302             : the database is not found.  In any event, all geokeys actually found in the
    2303             : file will be copied into the GTIFDefn.  However, if the database isn't
    2304             : found, codes implied by other codes will not be set properly.<p>
    2305             : 
    2306             : GTIFGetDefn() will not generally work if the EPSG derived database cannot
    2307             : be found.<p>
    2308             : 
    2309             : The normalization methodology operates by fetching tags from the GeoTIFF
    2310             : file, and then setting all other tags implied by them in the structure.  The
    2311             : implied relationships are worked out by reading definitions from the
    2312             : various EPSG derived database tables.<p>
    2313             : 
    2314             : For instance, if a PCS (ProjectedCSTypeGeoKey) is found in the GeoTIFF file
    2315             : this code is used to lookup a record in the database.
    2316             : For example given the PCS 26746 we can find the name
    2317             : (NAD27 / California zone VI), the GCS 4257 (NAD27), and the ProjectionCode
    2318             : 10406 (California CS27 zone VI).  The GCS, and ProjectionCode can in turn
    2319             : be looked up in other tables until all the details of units, ellipsoid,
    2320             : prime meridian, datum, projection (LambertConfConic_2SP) and projection
    2321             : parameters are established.  A full listgeo dump of a file
    2322             : for this result might look like the following, all based on a single PCS
    2323             : value:<p>
    2324             : 
    2325             : <pre>
    2326             : % listgeo -norm ~/data/geotiff/pci_eg/spaf27.tif
    2327             : Geotiff_Information:
    2328             :    Version: 1
    2329             :    Key_Revision: 1.0
    2330             :    Tagged_Information:
    2331             :       ModelTiepointTag (2,3):
    2332             :          0                0                0
    2333             :          1577139.71       634349.176       0
    2334             :       ModelPixelScaleTag (1,3):
    2335             :          195.509321       198.32184        0
    2336             :       End_Of_Tags.
    2337             :    Keyed_Information:
    2338             :       GTModelTypeGeoKey (Short,1): ModelTypeProjected
    2339             :       GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
    2340             :       ProjectedCSTypeGeoKey (Short,1): PCS_NAD27_California_VI
    2341             :       End_Of_Keys.
    2342             :    End_Of_Geotiff.
    2343             : 
    2344             : PCS = 26746 (NAD27 / California zone VI)
    2345             : Projection = 10406 (California CS27 zone VI)
    2346             : Projection Method: CT_LambertConfConic_2SP
    2347             :    ProjStdParallel1GeoKey: 33.883333
    2348             :    ProjStdParallel2GeoKey: 32.766667
    2349             :    ProjFalseOriginLatGeoKey: 32.166667
    2350             :    ProjFalseOriginLongGeoKey: -116.233333
    2351             :    ProjFalseEastingGeoKey: 609601.219202
    2352             :    ProjFalseNorthingGeoKey: 0.000000
    2353             : GCS: 4267/NAD27
    2354             : Datum: 6267/North American Datum 1927
    2355             : Ellipsoid: 7008/Clarke 1866 (6378206.40,6356583.80)
    2356             : Prime Meridian: 8901/Greenwich (0.000000)
    2357             : Projection Linear Units: 9003/US survey foot (0.304801m)
    2358             : </pre>
    2359             : 
    2360             : Note that GTIFGetDefn() does not inspect or return the tiepoints and scale.
    2361             : This must be handled separately as it normally would.  It is intended to
    2362             : simplify capture and normalization of the coordinate system definition.
    2363             : Note that GTIFGetDefn() also does the following things:
    2364             : 
    2365             : <ol>
    2366             : <li> Convert all angular values to decimal degrees.
    2367             : <li> Convert all linear values to meters.
    2368             : <li> Return the linear units and conversion to meters for the tiepoints and
    2369             : scale (though the tiepoints and scale remain in their native units).
    2370             : <li> When reading projection parameters a variety of differences between
    2371             : different GeoTIFF generators are handled, and a normalized set of parameters
    2372             : for each projection are always returned.
    2373             : </ol>
    2374             : 
    2375             : Code fields in the GTIFDefn are filled with KvUserDefined if there is not
    2376             : value to assign.  The parameter lists for each of the underlying projection
    2377             : transform methods can be found at the
    2378             : <a href="http://www.remotesensing.org/geotiff/proj_list">Projections</a>
    2379             : page.  Note that nParms will be set based on the maximum parameter used.
    2380             : Some of the parameters may not be used in which case the
    2381             : GTIFDefn::ProjParmId[] will
    2382             : be zero.  This is done to retain correspondence to the EPSG parameter
    2383             : numbering scheme.<p>
    2384             : 
    2385             : The
    2386             : <a href="http://www.remotesensing.org/cgi-bin/cvsweb.cgi/~checkout~/osrs/geotiff/libgeotiff/geotiff_proj4.c">geotiff_proj4.c</a> module distributed with libgeotiff can
    2387             : be used as an example of code that converts a GTIFDefn into another projection
    2388             : system.<p>
    2389             : 
    2390             : @see GTIFKeySet()
    2391             : 
    2392             : */
    2393             : 
    2394       10037 : int GTIFGetDefn( GTIF * psGTIF, GTIFDefn * psDefn )
    2395             : 
    2396             : {
    2397       10037 :     if( !GTIFGetPROJContext(psGTIF, TRUE, NULL) )
    2398             :     {
    2399           0 :         return FALSE;
    2400             :     }
    2401             : 
    2402             : /* -------------------------------------------------------------------- */
    2403             : /*      Initially we default all the information we can.                */
    2404             : /* -------------------------------------------------------------------- */
    2405       10037 :     psDefn->DefnSet = 1;
    2406       10037 :     psDefn->Model = KvUserDefined;
    2407       10037 :     psDefn->PCS = KvUserDefined;
    2408       10037 :     psDefn->GCS = KvUserDefined;
    2409       10037 :     psDefn->UOMLength = KvUserDefined;
    2410       10037 :     psDefn->UOMLengthInMeters = 1.0;
    2411       10037 :     psDefn->UOMAngle = KvUserDefined;
    2412       10037 :     psDefn->UOMAngleInDegrees = 1.0;
    2413       10037 :     psDefn->Datum = KvUserDefined;
    2414       10037 :     psDefn->Ellipsoid = KvUserDefined;
    2415       10037 :     psDefn->SemiMajor = 0.0;
    2416       10037 :     psDefn->SemiMinor = 0.0;
    2417       10037 :     psDefn->PM = KvUserDefined;
    2418       10037 :     psDefn->PMLongToGreenwich = 0.0;
    2419             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    2420       10037 :     psDefn->TOWGS84Count = 0;
    2421       10037 :     memset( psDefn->TOWGS84, 0, sizeof(psDefn->TOWGS84) );
    2422             : #endif
    2423             : 
    2424       10037 :     psDefn->ProjCode = KvUserDefined;
    2425       10037 :     psDefn->Projection = KvUserDefined;
    2426       10037 :     psDefn->CTProjection = KvUserDefined;
    2427             : 
    2428       10037 :     psDefn->nParms = 0;
    2429      110407 :     for( int i = 0; i < MAX_GTIF_PROJPARMS; i++ )
    2430             :     {
    2431      100370 :         psDefn->ProjParm[i] = 0.0;
    2432      100370 :         psDefn->ProjParmId[i] = 0;
    2433             :     }
    2434             : 
    2435       10037 :     psDefn->MapSys = KvUserDefined;
    2436       10037 :     psDefn->Zone = 0;
    2437             : 
    2438             : /* -------------------------------------------------------------------- */
    2439             : /*      Do we have any geokeys?                                         */
    2440             : /* -------------------------------------------------------------------- */
    2441             :     {
    2442       10037 :         int     nKeyCount = 0;
    2443             :         int     anVersion[3];
    2444       10037 :         GTIFDirectoryInfo( psGTIF, anVersion, &nKeyCount );
    2445             : 
    2446       10037 :         if( nKeyCount == 0 )
    2447             :         {
    2448        2455 :             psDefn->DefnSet = 0;
    2449        2455 :             return FALSE;
    2450             :         }
    2451             :     }
    2452             : 
    2453             : /* -------------------------------------------------------------------- */
    2454             : /*  Try to get the overall model type.        */
    2455             : /* -------------------------------------------------------------------- */
    2456        7582 :     GTIFKeyGetSSHORT(psGTIF,GTModelTypeGeoKey,&(psDefn->Model));
    2457             : 
    2458             : /* -------------------------------------------------------------------- */
    2459             : /*  Extract the Geog units.           */
    2460             : /* -------------------------------------------------------------------- */
    2461        7582 :     short nGeogUOMLinear = 9001; /* Linear_Meter */
    2462        7582 :     if( GTIFKeyGetSSHORT(psGTIF, GeogLinearUnitsGeoKey, &nGeogUOMLinear) == 1 )
    2463             :     {
    2464          23 :         psDefn->UOMLength = nGeogUOMLinear;
    2465             :     }
    2466             : 
    2467             : /* -------------------------------------------------------------------- */
    2468             : /*      Try to get a PCS.                                               */
    2469             : /* -------------------------------------------------------------------- */
    2470        7582 :     if( GTIFKeyGetSSHORT(psGTIF,ProjectedCSTypeGeoKey, &(psDefn->PCS)) == 1
    2471        3306 :         && psDefn->PCS != KvUserDefined )
    2472             :     {
    2473             :         /*
    2474             :          * Translate this into useful information.
    2475             :          */
    2476        3205 :         GTIFGetPCSInfoEx( psGTIF->pj_context,
    2477        3205 :                           psDefn->PCS, NULL, &(psDefn->ProjCode),
    2478             :                           &(psDefn->UOMLength), &(psDefn->GCS) );
    2479             :     }
    2480             : 
    2481             : /* -------------------------------------------------------------------- */
    2482             : /*       If we have the PCS code, but didn't find it in the database    */
    2483             : /*      (likely because we can't find them) we will try some ``jiffy    */
    2484             : /*      rules'' for UTM and state plane.                                */
    2485             : /* -------------------------------------------------------------------- */
    2486        7582 :     if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined )
    2487             :     {
    2488             :         int nZone;
    2489           0 :         int nGCS = psDefn->GCS;
    2490             : 
    2491           0 :         const int nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone );
    2492           0 :         if( nMapSys != KvUserDefined )
    2493             :         {
    2494           0 :             psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone );
    2495           0 :             psDefn->GCS = (short) nGCS;
    2496             :         }
    2497             :     }
    2498             : 
    2499             : /* -------------------------------------------------------------------- */
    2500             : /*      If the Proj_ code is specified directly, use that.              */
    2501             : /* -------------------------------------------------------------------- */
    2502        7582 :     if( psDefn->ProjCode == KvUserDefined )
    2503        4377 :         GTIFKeyGetSSHORT(psGTIF, ProjectionGeoKey, &(psDefn->ProjCode));
    2504             : 
    2505        7582 :     if( psDefn->ProjCode != KvUserDefined )
    2506             :     {
    2507             :         /*
    2508             :          * We have an underlying projection transformation value.  Look
    2509             :          * this up.  For a PCS of ``WGS 84 / UTM 11'' the transformation
    2510             :          * would be Transverse Mercator, with a particular set of options.
    2511             :          * The nProjTRFCode itself would correspond to the name
    2512             :          * ``UTM zone 11N'', and doesn't include datum info.
    2513             :          */
    2514        3219 :         GTIFGetProjTRFInfoEx( psGTIF->pj_context,
    2515        3219 :                               psDefn->ProjCode, NULL, &(psDefn->Projection),
    2516        3219 :                               psDefn->ProjParm );
    2517             : 
    2518             :         /*
    2519             :          * Set the GeoTIFF identity of the parameters.
    2520             :          */
    2521        3219 :         psDefn->CTProjection = (short)
    2522        3219 :             EPSGProjMethodToCTProjMethod( psDefn->Projection, FALSE );
    2523             : 
    2524        3219 :         SetGTParamIds( EPSGProjMethodToCTProjMethod(psDefn->Projection, TRUE),
    2525        3219 :                       psDefn->Projection,
    2526        3219 :                       psDefn->ProjParmId, NULL);
    2527        3219 :         psDefn->nParms = 7;
    2528             :     }
    2529             : 
    2530             : /* -------------------------------------------------------------------- */
    2531             : /*      Try to get a GCS.  If found, it will override any implied by    */
    2532             : /*      the PCS.                                                        */
    2533             : /* -------------------------------------------------------------------- */
    2534        7582 :     GTIFKeyGetSSHORT(psGTIF, GeographicTypeGeoKey, &(psDefn->GCS));
    2535        7582 :     if( psDefn->GCS < 1 || psDefn->GCS >= KvUserDefined )
    2536         435 :         psDefn->GCS = KvUserDefined;
    2537             : 
    2538             : /* -------------------------------------------------------------------- */
    2539             : /*      Derive the datum, and prime meridian from the GCS.              */
    2540             : /* -------------------------------------------------------------------- */
    2541        7582 :     if( psDefn->GCS != KvUserDefined )
    2542             :     {
    2543        7147 :         GTIFGetGCSInfoEx( psGTIF->pj_context,
    2544        7147 :                           psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM),
    2545             :                           &(psDefn->UOMAngle) );
    2546             :     }
    2547             : 
    2548             : /* -------------------------------------------------------------------- */
    2549             : /*      Handle the GCS angular units.  GeogAngularUnitsGeoKey           */
    2550             : /*      overrides the GCS or PCS setting.                               */
    2551             : /* -------------------------------------------------------------------- */
    2552        7582 :     GTIFKeyGetSSHORT(psGTIF, GeogAngularUnitsGeoKey, &(psDefn->UOMAngle));
    2553        7582 :     if( psDefn->UOMAngle != KvUserDefined )
    2554             :     {
    2555        7550 :         GTIFGetUOMAngleInfoEx( psGTIF->pj_context,
    2556        7550 :                                psDefn->UOMAngle, NULL,
    2557             :                                &(psDefn->UOMAngleInDegrees) );
    2558             :     }
    2559             : 
    2560             : /* -------------------------------------------------------------------- */
    2561             : /*      Check for a datum setting, and then use the datum to derive     */
    2562             : /*      an ellipsoid.                                                   */
    2563             : /* -------------------------------------------------------------------- */
    2564        7582 :     GTIFKeyGetSSHORT(psGTIF, GeogGeodeticDatumGeoKey, &(psDefn->Datum));
    2565             : 
    2566        7582 :     if( psDefn->Datum != KvUserDefined )
    2567             :     {
    2568        7169 :         GTIFGetDatumInfoEx( psGTIF->pj_context,
    2569        7169 :                             psDefn->Datum, NULL, &(psDefn->Ellipsoid) );
    2570             :     }
    2571             : 
    2572             : /* -------------------------------------------------------------------- */
    2573             : /*      Check for an explicit ellipsoid.  Use the ellipsoid to          */
    2574             : /*      derive the ellipsoid characteristics, if possible.              */
    2575             : /* -------------------------------------------------------------------- */
    2576        7582 :     GTIFKeyGetSSHORT(psGTIF, GeogEllipsoidGeoKey, &(psDefn->Ellipsoid));
    2577             : 
    2578        7582 :     if( psDefn->Ellipsoid != KvUserDefined )
    2579             :     {
    2580        7172 :         GTIFGetEllipsoidInfoEx( psGTIF->pj_context,
    2581        7172 :                                 psDefn->Ellipsoid, NULL,
    2582             :                                 &(psDefn->SemiMajor), &(psDefn->SemiMinor) );
    2583             :     }
    2584             : 
    2585             : /* -------------------------------------------------------------------- */
    2586             : /*      Check for overridden ellipsoid parameters.  It would be nice    */
    2587             : /*      to warn if they conflict with provided information, but for     */
    2588             : /*      now we just override.                                           */
    2589             : /* -------------------------------------------------------------------- */
    2590        7582 :     CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 ));
    2591        7582 :     CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogSemiMinorAxisGeoKey, &(psDefn->SemiMinor), 0, 1 ));
    2592             : 
    2593             :     double dfInvFlattening;
    2594        7582 :     if( GTIFKeyGetDOUBLE(psGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening,
    2595             :                    0, 1 ) == 1 )
    2596             :     {
    2597        3607 :         if( dfInvFlattening != 0.0 )
    2598        3607 :             psDefn->SemiMinor =
    2599        3607 :                 psDefn->SemiMajor * (1 - 1.0/dfInvFlattening);
    2600             :         else
    2601           0 :             psDefn->SemiMinor = psDefn->SemiMajor;
    2602             :     }
    2603             : 
    2604             : /* -------------------------------------------------------------------- */
    2605             : /*      Get the prime meridian info.                                    */
    2606             : /* -------------------------------------------------------------------- */
    2607        7582 :     GTIFKeyGetSSHORT(psGTIF, GeogPrimeMeridianGeoKey, &(psDefn->PM));
    2608             : 
    2609        7582 :     if( psDefn->PM != KvUserDefined )
    2610             :     {
    2611        7149 :         GTIFGetPMInfoEx( psGTIF->pj_context,
    2612        7149 :                          psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) );
    2613             :     }
    2614             :     else
    2615             :     {
    2616         433 :         CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogPrimeMeridianLongGeoKey,
    2617             :                    &(psDefn->PMLongToGreenwich), 0, 1 ));
    2618             : 
    2619         433 :         psDefn->PMLongToGreenwich =
    2620         433 :             GTIFAngleToDD( psDefn->PMLongToGreenwich,
    2621         433 :                            psDefn->UOMAngle );
    2622             :     }
    2623             : 
    2624             : /* -------------------------------------------------------------------- */
    2625             : /*      Get the TOWGS84 parameters.                                     */
    2626             : /* -------------------------------------------------------------------- */
    2627             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    2628        7582 :     psDefn->TOWGS84Count =
    2629        7582 :         (short)GTIFKeyGetDOUBLE(psGTIF, GeogTOWGS84GeoKey, psDefn->TOWGS84, 0, 7 );
    2630             : #endif
    2631             : 
    2632             : /* -------------------------------------------------------------------- */
    2633             : /*      Have the projection units of measure been overridden?  We       */
    2634             : /*      should likely be doing something about angular units too,       */
    2635             : /*      but these are very rarely not decimal degrees for actual        */
    2636             : /*      file coordinates.                                               */
    2637             : /* -------------------------------------------------------------------- */
    2638        7582 :     GTIFKeyGetSSHORT(psGTIF,ProjLinearUnitsGeoKey,&(psDefn->UOMLength));
    2639             : 
    2640        7582 :     if( psDefn->UOMLength != KvUserDefined )
    2641             :     {
    2642        3630 :         GTIFGetUOMLengthInfoEx( psGTIF->pj_context,
    2643        3630 :                                 psDefn->UOMLength, NULL,
    2644             :                                 &(psDefn->UOMLengthInMeters) );
    2645             :     }
    2646             :     else
    2647             :     {
    2648        3952 :         CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF,ProjLinearUnitSizeGeoKey,&(psDefn->UOMLengthInMeters),0,1));
    2649             :     }
    2650             : 
    2651             : /* -------------------------------------------------------------------- */
    2652             : /*      Handle a variety of user defined transform types.               */
    2653             : /* -------------------------------------------------------------------- */
    2654        7582 :     if( GTIFKeyGetSSHORT(psGTIF,ProjCoordTransGeoKey,
    2655             :                    &(psDefn->CTProjection)) == 1)
    2656             :     {
    2657          86 :         GTIFFetchProjParms( psGTIF, psDefn );
    2658             :     }
    2659             : 
    2660             : /* -------------------------------------------------------------------- */
    2661             : /*      Try to set the zoned map system information.                    */
    2662             : /* -------------------------------------------------------------------- */
    2663        7582 :     psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) );
    2664             : 
    2665             : /* -------------------------------------------------------------------- */
    2666             : /*      If this is UTM, and we were unable to extract the projection    */
    2667             : /*      parameters from the database just set them directly now,        */
    2668             : /*      since it's pretty easy, and a common case.                      */
    2669             : /* -------------------------------------------------------------------- */
    2670        7582 :     if( (psDefn->MapSys == MapSys_UTM_North
    2671        4522 :          || psDefn->MapSys == MapSys_UTM_South)
    2672        3063 :         && psDefn->CTProjection == KvUserDefined )
    2673             :     {
    2674           0 :         psDefn->CTProjection = CT_TransverseMercator;
    2675           0 :         psDefn->nParms = 7;
    2676           0 :         psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
    2677           0 :         psDefn->ProjParm[0] = 0.0;
    2678             : 
    2679           0 :         psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
    2680           0 :         psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0;
    2681             : 
    2682           0 :         psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
    2683           0 :         psDefn->ProjParm[4] = 0.9996;
    2684             : 
    2685           0 :         psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
    2686           0 :         psDefn->ProjParm[5] = 500000.0;
    2687             : 
    2688           0 :         psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
    2689             : 
    2690           0 :         if( psDefn->MapSys == MapSys_UTM_North )
    2691           0 :             psDefn->ProjParm[6] = 0.0;
    2692             :         else
    2693           0 :             psDefn->ProjParm[6] = 10000000.0;
    2694             :     }
    2695             : 
    2696        7582 :     return TRUE;
    2697             : }
    2698             : 
    2699             : /************************************************************************/
    2700             : /*                            GTIFDecToDMS()                            */
    2701             : /*                                                                      */
    2702             : /*      Convenient function to translate decimal degrees to DMS         */
    2703             : /*      format for reporting to a user.                                 */
    2704             : /************************************************************************/
    2705             : 
    2706           0 : const char *GTIFDecToDMS( double dfAngle, const char * pszAxis,
    2707             :                           int nPrecision )
    2708             : {
    2709           0 :     if( !(dfAngle >= -360 && dfAngle <= 360) )
    2710           0 :         return "";
    2711             : 
    2712           0 :     double dfRound = 0.5/60;
    2713           0 :     for( int i = 0; i < nPrecision; i++ )
    2714           0 :         dfRound = dfRound * 0.1;
    2715             : 
    2716           0 :     int nDegrees = (int) ABS(dfAngle);
    2717           0 :     int nMinutes = (int) ((ABS(dfAngle) - nDegrees) * 60 + dfRound);
    2718           0 :     if( nMinutes == 60 )
    2719             :     {
    2720           0 :         nDegrees ++;
    2721           0 :         nMinutes = 0;
    2722             :     }
    2723           0 :     const double dfSeconds = ABS((ABS(dfAngle) * 3600 - nDegrees*3600 - nMinutes*60));
    2724             : 
    2725           0 :     const char  *pszHemisphere = NULL;
    2726           0 :     if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 )
    2727           0 :         pszHemisphere = "W";
    2728           0 :     else if( EQUAL(pszAxis,"Long") )
    2729           0 :         pszHemisphere = "E";
    2730           0 :     else if( dfAngle < 0.0 )
    2731           0 :         pszHemisphere = "S";
    2732             :     else
    2733           0 :         pszHemisphere = "N";
    2734             : 
    2735             :     char szFormat[30];
    2736           0 :     snprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
    2737             :              nPrecision+3, nPrecision, pszHemisphere);
    2738             :     static char szBuffer[50];
    2739           0 :     snprintf(
    2740             :         szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes, dfSeconds);
    2741             : 
    2742           0 :     return szBuffer;
    2743             : }
    2744             : 
    2745             : /************************************************************************/
    2746             : /*                           GTIFPrintDefn()                            */
    2747             : /*                                                                      */
    2748             : /*      Report the contents of a GTIFDefn structure ... mostly for      */
    2749             : /*      debugging.                                                      */
    2750             : /************************************************************************/
    2751             : 
    2752           0 : void GTIFPrintDefnEx( GTIF *psGTIF, GTIFDefn * psDefn, FILE * fp )
    2753             : 
    2754             : {
    2755           0 :     GTIFGetPROJContext(psGTIF, TRUE, NULL);
    2756             : 
    2757             : /* -------------------------------------------------------------------- */
    2758             : /*      Do we have anything to report?                                  */
    2759             : /* -------------------------------------------------------------------- */
    2760           0 :     if( !psDefn->DefnSet )
    2761             :     {
    2762           0 :         fprintf( fp, "No GeoKeys found.\n" );
    2763           0 :         return;
    2764             :     }
    2765             : 
    2766             : /* -------------------------------------------------------------------- */
    2767             : /*      Get the PCS name if possible.                                   */
    2768             : /* -------------------------------------------------------------------- */
    2769           0 :     if( psDefn->PCS != KvUserDefined )
    2770             :     {
    2771           0 :         char  *pszPCSName = NULL;
    2772             : 
    2773           0 :         if( psGTIF->pj_context )
    2774             :         {
    2775           0 :             GTIFGetPCSInfoEx( psGTIF->pj_context,
    2776           0 :                               psDefn->PCS, &pszPCSName, NULL, NULL, NULL );
    2777             :         }
    2778           0 :         if( pszPCSName == NULL )
    2779           0 :             pszPCSName = CPLStrdup("name unknown");
    2780             : 
    2781           0 :         fprintf( fp, "PCS = %d (%s)\n", psDefn->PCS, pszPCSName );
    2782           0 :         CPLFree( pszPCSName );
    2783             :     }
    2784             : 
    2785             : /* -------------------------------------------------------------------- */
    2786             : /*  Dump the projection code if possible.       */
    2787             : /* -------------------------------------------------------------------- */
    2788           0 :     if( psDefn->ProjCode != KvUserDefined )
    2789             :     {
    2790           0 :         char  *pszTRFName = NULL;
    2791             : 
    2792           0 :         if( psGTIF->pj_context )
    2793             :         {
    2794           0 :             GTIFGetProjTRFInfoEx( psGTIF->pj_context,
    2795           0 :                                   psDefn->ProjCode, &pszTRFName, NULL, NULL );
    2796             :         }
    2797           0 :         if( pszTRFName == NULL )
    2798           0 :             pszTRFName = CPLStrdup("");
    2799             : 
    2800           0 :         fprintf( fp, "Projection = %d (%s)\n",
    2801           0 :                  psDefn->ProjCode, pszTRFName );
    2802             : 
    2803           0 :         CPLFree( pszTRFName );
    2804             :     }
    2805             : 
    2806             : /* -------------------------------------------------------------------- */
    2807             : /*      Try to dump the projection method name, and parameters if possible.*/
    2808             : /* -------------------------------------------------------------------- */
    2809           0 :     if( psDefn->CTProjection != KvUserDefined )
    2810             :     {
    2811             :         const char *pszProjectionMethodName =
    2812           0 :             GTIFValueNameEx(psGTIF,
    2813             :                             ProjCoordTransGeoKey,
    2814           0 :                             psDefn->CTProjection);
    2815             : 
    2816           0 :         if( pszProjectionMethodName == NULL )
    2817           0 :             pszProjectionMethodName = "(unknown)";
    2818             : 
    2819           0 :         fprintf( fp, "Projection Method: %s\n", pszProjectionMethodName );
    2820             : 
    2821           0 :         for( int i = 0; i < psDefn->nParms; i++ )
    2822             :         {
    2823           0 :             if( psDefn->ProjParmId[i] == 0 )
    2824           0 :                 continue;
    2825             : 
    2826           0 :             char* pszName = GTIFKeyName((geokey_t) psDefn->ProjParmId[i]);
    2827           0 :             if( pszName == NULL )
    2828           0 :                 pszName = "(unknown)";
    2829             : 
    2830           0 :             if( i < 4 )
    2831             :             {
    2832             :                 char  *pszAxisName;
    2833             : 
    2834           0 :                 if( strstr(pszName,"Long") != NULL )
    2835           0 :                     pszAxisName = "Long";
    2836           0 :                 else if( strstr(pszName,"Lat") != NULL )
    2837           0 :                     pszAxisName = "Lat";
    2838             :                 else
    2839           0 :                     pszAxisName = "?";
    2840             : 
    2841           0 :                 fprintf( fp, "   %s: %f (%s)\n",
    2842             :                          pszName, psDefn->ProjParm[i],
    2843             :                          GTIFDecToDMS( psDefn->ProjParm[i], pszAxisName, 2 ) );
    2844             :             }
    2845           0 :             else if( i == 4 )
    2846           0 :                 fprintf( fp, "   %s: %f\n", pszName, psDefn->ProjParm[i] );
    2847             :             else
    2848           0 :                 fprintf( fp, "   %s: %f m\n", pszName, psDefn->ProjParm[i] );
    2849             :         }
    2850             :     }
    2851             : 
    2852             : /* -------------------------------------------------------------------- */
    2853             : /*      Report the GCS name, and number.                                */
    2854             : /* -------------------------------------------------------------------- */
    2855           0 :     if( psDefn->GCS != KvUserDefined )
    2856             :     {
    2857           0 :         char  *pszName = NULL;
    2858             : 
    2859           0 :         if( psGTIF->pj_context )
    2860             :         {
    2861           0 :             GTIFGetGCSInfoEx( psGTIF->pj_context,
    2862           0 :                               psDefn->GCS, &pszName, NULL, NULL, NULL );
    2863             :         }
    2864           0 :         if( pszName == NULL )
    2865           0 :             pszName = CPLStrdup("(unknown)");
    2866             : 
    2867           0 :         fprintf( fp, "GCS: %d/%s\n", psDefn->GCS, pszName );
    2868           0 :         CPLFree( pszName );
    2869             :     }
    2870             : 
    2871             : /* -------------------------------------------------------------------- */
    2872             : /*      Report the datum name.                                          */
    2873             : /* -------------------------------------------------------------------- */
    2874           0 :     if( psDefn->Datum != KvUserDefined )
    2875             :     {
    2876           0 :         char  *pszName = NULL;
    2877             : 
    2878           0 :         if( psGTIF->pj_context )
    2879             :         {
    2880           0 :             GTIFGetDatumInfoEx( psGTIF->pj_context,
    2881           0 :                                 psDefn->Datum, &pszName, NULL );
    2882             :         }
    2883           0 :         if( pszName == NULL )
    2884           0 :             pszName = CPLStrdup("(unknown)");
    2885             : 
    2886           0 :         fprintf( fp, "Datum: %d/%s\n", psDefn->Datum, pszName );
    2887           0 :         CPLFree( pszName );
    2888             :     }
    2889             : 
    2890             : /* -------------------------------------------------------------------- */
    2891             : /*      Report the ellipsoid.                                           */
    2892             : /* -------------------------------------------------------------------- */
    2893           0 :     if( psDefn->Ellipsoid != KvUserDefined )
    2894             :     {
    2895           0 :         char  *pszName = NULL;
    2896             : 
    2897           0 :         if( psGTIF->pj_context )
    2898             :         {
    2899           0 :             GTIFGetEllipsoidInfoEx( psGTIF->pj_context,
    2900           0 :                                     psDefn->Ellipsoid, &pszName, NULL, NULL );
    2901             :         }
    2902           0 :         if( pszName == NULL )
    2903           0 :             pszName = CPLStrdup("(unknown)");
    2904             : 
    2905           0 :         fprintf( fp, "Ellipsoid: %d/%s (%.2f,%.2f)\n",
    2906           0 :                  psDefn->Ellipsoid, pszName,
    2907             :                  psDefn->SemiMajor, psDefn->SemiMinor );
    2908           0 :         CPLFree( pszName );
    2909             :     }
    2910             : 
    2911             : /* -------------------------------------------------------------------- */
    2912             : /*      Report the prime meridian.                                      */
    2913             : /* -------------------------------------------------------------------- */
    2914           0 :     if( psDefn->PM != KvUserDefined )
    2915             :     {
    2916           0 :         char  *pszName = NULL;
    2917             : 
    2918           0 :         if( psGTIF->pj_context )
    2919             :         {
    2920           0 :             GTIFGetPMInfoEx( psGTIF->pj_context,
    2921           0 :                              psDefn->PM, &pszName, NULL );
    2922             :         }
    2923             : 
    2924           0 :         if( pszName == NULL )
    2925           0 :             pszName = CPLStrdup("(unknown)");
    2926             : 
    2927           0 :         fprintf( fp, "Prime Meridian: %d/%s (%f/%s)\n",
    2928           0 :                  psDefn->PM, pszName,
    2929             :                  psDefn->PMLongToGreenwich,
    2930             :                  GTIFDecToDMS( psDefn->PMLongToGreenwich, "Long", 2 ) );
    2931           0 :         CPLFree( pszName );
    2932             :     }
    2933             : 
    2934             : /* -------------------------------------------------------------------- */
    2935             : /*      Report TOWGS84 parameters.                                      */
    2936             : /* -------------------------------------------------------------------- */
    2937             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    2938           0 :     if( psDefn->TOWGS84Count > 0 )
    2939             :     {
    2940           0 :         fprintf( fp, "TOWGS84: " );
    2941             : 
    2942           0 :         for( int i = 0; i < psDefn->TOWGS84Count; i++ )
    2943             :         {
    2944           0 :             if( i > 0 )
    2945           0 :                 fprintf( fp, "," );
    2946           0 :             fprintf( fp, "%g", psDefn->TOWGS84[i] );
    2947             :         }
    2948             : 
    2949           0 :         fprintf( fp, "\n" );
    2950             :     }
    2951             : #endif
    2952             : 
    2953             : /* -------------------------------------------------------------------- */
    2954             : /*      Report the projection units of measure (currently just          */
    2955             : /*      linear).                                                        */
    2956             : /* -------------------------------------------------------------------- */
    2957           0 :     if( psDefn->UOMLength != KvUserDefined )
    2958             :     {
    2959           0 :         char  *pszName = NULL;
    2960             : 
    2961           0 :         if( psGTIF->pj_context )
    2962             :         {
    2963           0 :             GTIFGetUOMLengthInfoEx(
    2964           0 :                 psGTIF->pj_context, psDefn->UOMLength, &pszName, NULL );
    2965             :         }
    2966           0 :         if( pszName == NULL )
    2967           0 :             pszName = CPLStrdup( "(unknown)" );
    2968             : 
    2969           0 :         fprintf( fp, "Projection Linear Units: %d/%s (%fm)\n",
    2970           0 :                  psDefn->UOMLength, pszName, psDefn->UOMLengthInMeters );
    2971           0 :         CPLFree( pszName );
    2972             :     }
    2973             :     else
    2974             :     {
    2975           0 :         fprintf( fp, "Projection Linear Units: User-Defined (%fm)\n",
    2976             :                  psDefn->UOMLengthInMeters );
    2977             :     }
    2978             : }
    2979             : 
    2980           0 : void GTIFPrintDefn( GTIFDefn * psDefn, FILE * fp )
    2981             : {
    2982           0 :     GTIF *psGTIF = GTIFNew(NULL);
    2983           0 :     if( psGTIF )
    2984             :     {
    2985           0 :         GTIFPrintDefnEx(psGTIF, psDefn, fp);
    2986           0 :         GTIFFree(psGTIF);
    2987             :     }
    2988           0 : }
    2989             : 
    2990             : /************************************************************************/
    2991             : /*                           GTIFFreeMemory()                           */
    2992             : /*                                                                      */
    2993             : /*      Externally visible function to free memory allocated within     */
    2994             : /*      geo_normalize.c.                                                */
    2995             : /************************************************************************/
    2996             : 
    2997       42795 : void GTIFFreeMemory( char * pMemory )
    2998             : 
    2999             : {
    3000       42795 :     if( pMemory != NULL )
    3001       42776 :         VSIFree( pMemory );
    3002       42795 : }
    3003             : 
    3004             : /************************************************************************/
    3005             : /*                           GTIFAllocDefn()                            */
    3006             : /*                                                                      */
    3007             : /*      This allocates a GTIF structure in such a way that the          */
    3008             : /*      calling application doesn't need to know the size and           */
    3009             : /*      initializes it appropriately.                                   */
    3010             : /************************************************************************/
    3011             : 
    3012       10037 : GTIFDefn *GTIFAllocDefn()
    3013             : {
    3014       10037 :     return (GTIFDefn *) CPLCalloc(sizeof(GTIFDefn),1);
    3015             : }
    3016             : 
    3017             : /************************************************************************/
    3018             : /*                            GTIFFreeDefn()                            */
    3019             : /*                                                                      */
    3020             : /*      Free a GTIF structure allocated by GTIFAllocDefn().             */
    3021             : /************************************************************************/
    3022             : 
    3023       10037 : void GTIFFreeDefn( GTIFDefn *defn )
    3024             : {
    3025       10037 :     VSIFree( defn );
    3026       10036 : }
    3027             : 
    3028             : /************************************************************************/
    3029             : /*                       GTIFAttachPROJContext()                        */
    3030             : /*                                                                      */
    3031             : /*      Attach an existing PROJ context to the GTIF handle, but         */
    3032             : /*      ownership of the context remains to the caller.                 */
    3033             : /************************************************************************/
    3034             : 
    3035       27109 : void GTIFAttachPROJContext( GTIF *psGTIF, void* pjContext )
    3036             : {
    3037       27109 :     if( psGTIF->own_pj_context )
    3038             :     {
    3039           0 :         proj_context_destroy(psGTIF->pj_context);
    3040             :     }
    3041       27109 :     psGTIF->own_pj_context = FALSE;
    3042       27109 :     psGTIF->pj_context = (PJ_CONTEXT*) pjContext;
    3043       27109 : }
    3044             : 
    3045             : /************************************************************************/
    3046             : /*                         GTIFGetPROJContext()                         */
    3047             : /*                                                                      */
    3048             : /*      Return the PROJ context attached to the GTIF handle.            */
    3049             : /*      If it has not yet been instantiated and instantiateIfNeeded=TRUE*/
    3050             : /*      then, it will be instantiated (and owned by GTIF handle).       */
    3051             : /************************************************************************/
    3052             : 
    3053       24862 : void *GTIFGetPROJContext( GTIF *psGTIF, int instantiateIfNeeded,
    3054             :                           int* out_gtif_own_pj_context )
    3055             : {
    3056       24862 :     if( psGTIF->pj_context || !instantiateIfNeeded )
    3057             :     {
    3058       24862 :         if( out_gtif_own_pj_context )
    3059             :         {
    3060           0 :             *out_gtif_own_pj_context = psGTIF->own_pj_context;
    3061             :         }
    3062       24862 :         return psGTIF->pj_context;
    3063             :     }
    3064           0 :     psGTIF->pj_context = proj_context_create();
    3065           0 :     psGTIF->own_pj_context = psGTIF->pj_context != NULL;
    3066           0 :     if( out_gtif_own_pj_context )
    3067             :     {
    3068           0 :         *out_gtif_own_pj_context = psGTIF->own_pj_context;
    3069             :     }
    3070           0 :     return psGTIF->pj_context;
    3071             : }
    3072             : 
    3073             : 
    3074           0 : void GTIFDeaccessCSV( void )
    3075             : {
    3076             :     /* No operation */
    3077           0 : }
    3078             : 
    3079             : #ifndef GDAL_COMPILATION
    3080             : void SetCSVFilenameHook( const char *(*CSVFileOverride)(const char *) )
    3081             : {
    3082             :     (void)CSVFileOverride;
    3083             :     /* No operation */
    3084             : }
    3085             : #endif

Generated by: LCOV version 1.14