EVOLUTION-MANAGER
Edit File: geo_normalize.c
/****************************************************************************** * $Id: geo_normalize.c 2741 2016-09-04 18:50:49Z rouault $ * * Project: libgeotiff * Purpose: Code to normalize PCS and other composite codes in a GeoTIFF file. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ #include "cpl_serv.h" #include "geo_tiffp.h" #include "geovalues.h" #include "geo_normalize.h" #ifndef KvUserDefined # define KvUserDefined 32767 #endif #ifndef M_PI # define M_PI 3.14159265358979323846 #endif /* EPSG Codes for projection parameters. Unfortunately, these bear no relationship to the GeoTIFF codes even though the names are so similar. */ #define EPSGNatOriginLat 8801 #define EPSGNatOriginLong 8802 #define EPSGNatOriginScaleFactor 8805 #define EPSGFalseEasting 8806 #define EPSGFalseNorthing 8807 #define EPSGProjCenterLat 8811 #define EPSGProjCenterLong 8812 #define EPSGAzimuth 8813 #define EPSGAngleRectifiedToSkewedGrid 8814 #define EPSGInitialLineScaleFactor 8815 #define EPSGProjCenterEasting 8816 #define EPSGProjCenterNorthing 8817 #define EPSGPseudoStdParallelLat 8818 #define EPSGPseudoStdParallelScaleFactor 8819 #define EPSGFalseOriginLat 8821 #define EPSGFalseOriginLong 8822 #define EPSGStdParallel1Lat 8823 #define EPSGStdParallel2Lat 8824 #define EPSGFalseOriginEasting 8826 #define EPSGFalseOriginNorthing 8827 #define EPSGSphericalOriginLat 8828 #define EPSGSphericalOriginLong 8829 #define EPSGInitialLongitude 8830 #define EPSGZoneWidth 8831 #define EPSGLatOfStdParallel 8832 #define EPSGOriginLong 8833 #define EPSGTopocentricOriginLat 8834 #define EPSGTopocentricOriginLong 8835 #define EPSGTopocentricOriginHeight 8836 #define CT_Ext_Mercator_2SP -CT_Mercator #ifndef CPL_INLINE # if (defined(__GNUC__) && !defined(__NO_INLINE__)) || defined(_MSC_VER) # define HAS_CPL_INLINE 1 # define CPL_INLINE __inline # elif defined(__SUNPRO_CC) # define HAS_CPL_INLINE 1 # define CPL_INLINE inline # else # define CPL_INLINE # endif #endif #ifndef CPL_UNUSED #if defined(__GNUC__) && __GNUC__ >= 4 # define CPL_UNUSED __attribute((__unused__)) #else /* TODO: add cases for other compilers */ # define CPL_UNUSED #endif #endif CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {} /************************************************************************/ /* GTIFGetPCSInfo() */ /************************************************************************/ int GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName, short *pnProjOp, short *pnUOMLengthCode, short *pnGeogCS ) { char **papszRecord; char szSearchKey[24]; const char *pszFilename; int nDatum; int nZone; int Proj = GTIFPCSToMapSys( nPCSCode, &nDatum, &nZone ); if ((Proj == MapSys_UTM_North || Proj == MapSys_UTM_South) && nDatum != KvUserDefined) { const char* pszDatumName = NULL; switch (nDatum) { case GCS_NAD27: pszDatumName = "NAD27"; break; case GCS_NAD83: pszDatumName = "NAD83"; break; case GCS_WGS_72: pszDatumName = "WGS 72"; break; case GCS_WGS_72BE: pszDatumName = "WGS 72BE"; break; case GCS_WGS_84: pszDatumName = "WGS 84"; break; default: break; } if (pszDatumName) { if (ppszEPSGName) { char szEPSGName[64]; sprintf(szEPSGName, "%s / UTM zone %d%c", pszDatumName, nZone, (Proj == MapSys_UTM_North) ? 'N' : 'S'); *ppszEPSGName = CPLStrdup(szEPSGName); } if (pnProjOp) *pnProjOp = (short) (((Proj == MapSys_UTM_North) ? Proj_UTM_zone_1N - 1 : Proj_UTM_zone_1S - 1) + nZone); if (pnUOMLengthCode) *pnUOMLengthCode = 9001; /* Linear_Meter */ if (pnGeogCS) *pnGeogCS = (short) nDatum; return TRUE; } } /* -------------------------------------------------------------------- */ /* Search the pcs.override table for this PCS. */ /* -------------------------------------------------------------------- */ pszFilename = CSVFilename( "pcs.override.csv" ); sprintf( szSearchKey, "%d", nPCSCode ); papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", szSearchKey, CC_Integer ); /* -------------------------------------------------------------------- */ /* If not found, search the EPSG PCS database. */ /* -------------------------------------------------------------------- */ if( papszRecord == NULL ) { pszFilename = CSVFilename( "pcs.csv" ); sprintf( szSearchKey, "%d", nPCSCode ); papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", szSearchKey, CC_Integer ); if( papszRecord == NULL ) { static int bWarnedOrTried = FALSE; if( !bWarnedOrTried ) { FILE* f = VSIFOpen(CSVFilename( "pcs.csv" ), "rb"); if( f == NULL ) CPLError(CE_Warning, CPLE_AppDefined, "Cannot find pcs.csv"); else VSIFClose(f); bWarnedOrTried = TRUE; } return FALSE; } } /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszEPSGName != NULL ) { *ppszEPSGName = CPLStrdup( CSLGetField( papszRecord, CSVGetFileFieldId(pszFilename, "COORD_REF_SYS_NAME") )); } /* -------------------------------------------------------------------- */ /* Get the UOM Length code, if requested. */ /* -------------------------------------------------------------------- */ if( pnUOMLengthCode != NULL ) { const char *pszValue; pszValue = CSLGetField( papszRecord, CSVGetFileFieldId(pszFilename,"UOM_CODE")); if( atoi(pszValue) > 0 ) *pnUOMLengthCode = (short) atoi(pszValue); else *pnUOMLengthCode = KvUserDefined; } /* -------------------------------------------------------------------- */ /* Get the UOM Length code, if requested. */ /* -------------------------------------------------------------------- */ if( pnProjOp != NULL ) { const char *pszValue; pszValue = CSLGetField( papszRecord, CSVGetFileFieldId(pszFilename,"COORD_OP_CODE")); if( atoi(pszValue) > 0 ) *pnProjOp = (short) atoi(pszValue); else *pnProjOp = KvUserDefined; } /* -------------------------------------------------------------------- */ /* Get the GeogCS (Datum with PM) code, if requested. */ /* -------------------------------------------------------------------- */ if( pnGeogCS != NULL ) { const char *pszValue; pszValue = CSLGetField( papszRecord, CSVGetFileFieldId(pszFilename,"SOURCE_GEOGCRS_CODE")); if( atoi(pszValue) > 0 ) *pnGeogCS = (short) atoi(pszValue); else *pnGeogCS = KvUserDefined; } return TRUE; } /************************************************************************/ /* GTIFAngleToDD() */ /* */ /* Convert a numeric angle to decimal degrees. */ /************************************************************************/ double GTIFAngleToDD( double dfAngle, int nUOMAngle ) { if( nUOMAngle == 9110 ) /* DDD.MMSSsss */ { char szAngleString[32]; sprintf( szAngleString, "%12.7f", dfAngle ); dfAngle = GTIFAngleStringToDD( szAngleString, nUOMAngle ); } else if ( nUOMAngle != KvUserDefined ) { double dfInDegrees = 1.0; GTIFGetUOMAngleInfo( nUOMAngle, NULL, &dfInDegrees ); dfAngle = dfAngle * dfInDegrees; } return dfAngle; } /************************************************************************/ /* GTIFAngleStringToDD() */ /* */ /* Convert an angle in the specified units to decimal degrees. */ /************************************************************************/ double GTIFAngleStringToDD( const char * pszAngle, int nUOMAngle ) { double dfAngle; if( nUOMAngle == 9110 ) /* DDD.MMSSsss */ { char *pszDecimal; dfAngle = ABS(atoi(pszAngle)); pszDecimal = strchr(pszAngle,'.'); if( pszDecimal != NULL && strlen(pszDecimal) > 1 ) { char szMinutes[3]; char szSeconds[64]; szMinutes[0] = pszDecimal[1]; if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' ) szMinutes[1] = pszDecimal[2]; else szMinutes[1] = '0'; szMinutes[2] = '\0'; dfAngle += atoi(szMinutes) / 60.0; if( strlen(pszDecimal) > 3 ) { szSeconds[0] = pszDecimal[3]; if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' ) { szSeconds[1] = pszDecimal[4]; szSeconds[2] = '.'; strncpy( szSeconds+3, pszDecimal + 5, sizeof(szSeconds) - 3 ); szSeconds[sizeof(szSeconds) - 1] = 0; } else { szSeconds[1] = '0'; szSeconds[2] = '\0'; } dfAngle += GTIFAtof(szSeconds) / 3600.0; } } if( pszAngle[0] == '-' ) dfAngle *= -1; } else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */ { dfAngle = 180 * (GTIFAtof(pszAngle ) / 200); } else if( nUOMAngle == 9101 ) /* radians */ { dfAngle = 180 * (GTIFAtof(pszAngle ) / M_PI); } else if( nUOMAngle == 9103 ) /* arc-minute */ { dfAngle = GTIFAtof(pszAngle) / 60; } else if( nUOMAngle == 9104 ) /* arc-second */ { dfAngle = GTIFAtof(pszAngle) / 3600; } else /* decimal degrees ... some cases missing but seemingly never used */ { CPLAssert( nUOMAngle == 9102 || nUOMAngle == KvUserDefined || nUOMAngle == 0 ); dfAngle = GTIFAtof(pszAngle ); } return dfAngle; } /************************************************************************/ /* GTIFGetGCSInfo() */ /* */ /* Fetch the datum, and prime meridian related to a particular */ /* GCS. */ /************************************************************************/ int GTIFGetGCSInfo( int nGCSCode, char ** ppszName, short * pnDatum, short * pnPM, short *pnUOMAngle ) { char szSearchKey[24]; int nDatum=0, nPM, nUOMAngle; const char *pszFilename; /* -------------------------------------------------------------------- */ /* Handle some "well known" GCS codes directly */ /* -------------------------------------------------------------------- */ const char * pszName = NULL; nPM = PM_Greenwich; nUOMAngle = Angular_DMS_Hemisphere; if( nGCSCode == GCS_NAD27 ) { nDatum = Datum_North_American_Datum_1927; pszName = "NAD27"; } else if( nGCSCode == GCS_NAD83 ) { nDatum = Datum_North_American_Datum_1983; pszName = "NAD83"; } else if( nGCSCode == GCS_WGS_84 ) { nDatum = Datum_WGS84; pszName = "WGS 84"; } else if( nGCSCode == GCS_WGS_72 ) { nDatum = Datum_WGS72; pszName = "WGS 72"; } else if ( nGCSCode == KvUserDefined ) { return FALSE; } if (pszName != NULL) { if( ppszName != NULL ) *ppszName = CPLStrdup( pszName ); if( pnDatum != NULL ) *pnDatum = (short) nDatum; if( pnPM != NULL ) *pnPM = (short) nPM; if( pnUOMAngle != NULL ) *pnUOMAngle = (short) nUOMAngle; return TRUE; } /* -------------------------------------------------------------------- */ /* Search the database for the corresponding datum code. */ /* -------------------------------------------------------------------- */ pszFilename = CSVFilename("gcs.override.csv"); sprintf( szSearchKey, "%d", nGCSCode ); nDatum = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", szSearchKey, CC_Integer, "DATUM_CODE" ) ); if( nDatum < 1 ) { pszFilename = CSVFilename("gcs.csv"); sprintf( szSearchKey, "%d", nGCSCode ); nDatum = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", szSearchKey, CC_Integer, "DATUM_CODE" ) ); } if( nDatum < 1 ) { static int bWarnedOrTried = FALSE; if( !bWarnedOrTried ) { FILE* f = VSIFOpen(CSVFilename( "gcs.csv" ), "rb"); if( f == NULL ) CPLError(CE_Warning, CPLE_AppDefined, "Cannot find gcs.csv"); else VSIFClose(f); bWarnedOrTried = TRUE; } return FALSE; } if( pnDatum != NULL ) *pnDatum = (short) nDatum; /* -------------------------------------------------------------------- */ /* Get the PM. */ /* -------------------------------------------------------------------- */ if( pnPM != NULL ) { nPM = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", szSearchKey, CC_Integer, "PRIME_MERIDIAN_CODE" ) ); if( nPM < 1 ) return FALSE; *pnPM = (short) nPM; } /* -------------------------------------------------------------------- */ /* Get the angular units. */ /* -------------------------------------------------------------------- */ nUOMAngle = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",szSearchKey, CC_Integer, "UOM_CODE" ) ); if( nUOMAngle < 1 ) return FALSE; if( pnUOMAngle != NULL ) *pnUOMAngle = (short) nUOMAngle; /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszName != NULL ) *ppszName = CPLStrdup(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",szSearchKey,CC_Integer, "COORD_REF_SYS_NAME" )); return TRUE; } /************************************************************************/ /* GTIFGetEllipsoidInfo() */ /* */ /* Fetch info about an ellipsoid. Axes are always returned in */ /* meters. SemiMajor computed based on inverse flattening */ /* where that is provided. */ /************************************************************************/ int GTIFGetEllipsoidInfo( int nEllipseCode, char ** ppszName, double * pdfSemiMajor, double * pdfSemiMinor ) { char szSearchKey[24]; double dfSemiMajor=0.0, dfToMeters = 1.0; int nUOMLength; /* -------------------------------------------------------------------- */ /* Try some well known ellipsoids. */ /* -------------------------------------------------------------------- */ double dfInvFlattening=0.0, dfSemiMinor=0.0; const char *pszName = NULL; if( nEllipseCode == Ellipse_Clarke_1866 ) { pszName = "Clarke 1866"; dfSemiMajor = 6378206.4; dfSemiMinor = 6356583.8; dfInvFlattening = 0.0; } else if( nEllipseCode == Ellipse_GRS_1980 ) { pszName = "GRS 1980"; dfSemiMajor = 6378137.0; dfSemiMinor = 0.0; dfInvFlattening = 298.257222101; } else if( nEllipseCode == Ellipse_WGS_84 ) { pszName = "WGS 84"; dfSemiMajor = 6378137.0; dfSemiMinor = 0.0; dfInvFlattening = 298.257223563; } else if( nEllipseCode == 7043 ) { pszName = "WGS 72"; dfSemiMajor = 6378135.0; dfSemiMinor = 0.0; dfInvFlattening = 298.26; } if (pszName != NULL) { if( dfSemiMinor == 0.0 ) dfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening); if( pdfSemiMinor != NULL ) *pdfSemiMinor = dfSemiMinor; if( pdfSemiMajor != NULL ) *pdfSemiMajor = dfSemiMajor; if( ppszName != NULL ) *ppszName = CPLStrdup( pszName ); return TRUE; } /* -------------------------------------------------------------------- */ /* Get the semi major axis. */ /* -------------------------------------------------------------------- */ sprintf( szSearchKey, "%d", nEllipseCode ); dfSemiMajor = GTIFAtof(CSVGetField( CSVFilename("ellipsoid.csv"), "ELLIPSOID_CODE", szSearchKey, CC_Integer, "SEMI_MAJOR_AXIS" ) ); if( dfSemiMajor == 0.0 ) { static int bWarnedOrTried = FALSE; if( !bWarnedOrTried ) { FILE* f = VSIFOpen(CSVFilename( "ellipsoid.csv" ), "rb"); if( f == NULL ) CPLError(CE_Warning, CPLE_AppDefined, "Cannot find ellipsoid.csv"); else VSIFClose(f); bWarnedOrTried = TRUE; } return FALSE; } /* -------------------------------------------------------------------- */ /* Get the translation factor into meters. */ /* -------------------------------------------------------------------- */ nUOMLength = atoi(CSVGetField( CSVFilename("ellipsoid.csv"), "ELLIPSOID_CODE", szSearchKey, CC_Integer, "UOM_CODE" )); GTIFGetUOMLengthInfo( nUOMLength, NULL, &dfToMeters ); dfSemiMajor *= dfToMeters; if( pdfSemiMajor != NULL ) *pdfSemiMajor = dfSemiMajor; /* -------------------------------------------------------------------- */ /* Get the semi-minor if requested. If the Semi-minor axis */ /* isn't available, compute it based on the inverse flattening. */ /* -------------------------------------------------------------------- */ if( pdfSemiMinor != NULL ) { *pdfSemiMinor = GTIFAtof(CSVGetField( CSVFilename("ellipsoid.csv"), "ELLIPSOID_CODE", szSearchKey, CC_Integer, "SEMI_MINOR_AXIS" )) * dfToMeters; if( *pdfSemiMinor == 0.0 ) { dfInvFlattening = GTIFAtof(CSVGetField( CSVFilename("ellipsoid.csv"), "ELLIPSOID_CODE", szSearchKey, CC_Integer, "INV_FLATTENING" )); *pdfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening); } } /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszName != NULL ) *ppszName = CPLStrdup(CSVGetField( CSVFilename("ellipsoid.csv"), "ELLIPSOID_CODE", szSearchKey, CC_Integer, "ELLIPSOID_NAME" )); return TRUE; } /************************************************************************/ /* GTIFGetPMInfo() */ /* */ /* Get the offset between a given prime meridian and Greenwich */ /* in degrees. */ /************************************************************************/ int GTIFGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset ) { char szSearchKey[24]; int nUOMAngle; const char *pszFilename; /* -------------------------------------------------------------------- */ /* Use a special short cut for Greenwich, since it is so common. */ /* -------------------------------------------------------------------- */ if( nPMCode == PM_Greenwich ) { if( pdfOffset != NULL ) *pdfOffset = 0.0; if( ppszName != NULL ) *ppszName = CPLStrdup( "Greenwich" ); return TRUE; } /* -------------------------------------------------------------------- */ /* Search the database for the corresponding datum code. */ /* -------------------------------------------------------------------- */ pszFilename = CSVFilename("prime_meridian.csv"); sprintf( szSearchKey, "%d", nPMCode ); nUOMAngle = atoi(CSVGetField( pszFilename, "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, "UOM_CODE" ) ); if( nUOMAngle < 1 ) { static int bWarnedOrTried = FALSE; if( !bWarnedOrTried ) { FILE* f = VSIFOpen(CSVFilename( "prime_meridian.csv" ), "rb"); if( f == NULL ) CPLError(CE_Warning, CPLE_AppDefined, "Cannot find prime_meridian.csv"); else VSIFClose(f); bWarnedOrTried = TRUE; } return FALSE; } /* -------------------------------------------------------------------- */ /* Get the PM offset. */ /* -------------------------------------------------------------------- */ if( pdfOffset != NULL ) { *pdfOffset = GTIFAngleStringToDD( CSVGetField( pszFilename, "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, "GREENWICH_LONGITUDE" ), nUOMAngle ); } /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszName != NULL ) *ppszName = CPLStrdup( CSVGetField( pszFilename, "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, "PRIME_MERIDIAN_NAME" )); return TRUE; } /************************************************************************/ /* GTIFGetDatumInfo() */ /* */ /* Fetch the ellipsoid, and name for a datum. */ /************************************************************************/ int GTIFGetDatumInfo( int nDatumCode, char ** ppszName, short * pnEllipsoid ) { char szSearchKey[24]; int nEllipsoid = 0; const char *pszFilename; FILE *fp; const char *pszName = NULL; /* -------------------------------------------------------------------- */ /* Handle a few built-in datums. */ /* -------------------------------------------------------------------- */ if( nDatumCode == Datum_North_American_Datum_1927 ) { nEllipsoid = Ellipse_Clarke_1866; pszName = "North American Datum 1927"; } else if( nDatumCode == Datum_North_American_Datum_1983 ) { nEllipsoid = Ellipse_GRS_1980; pszName = "North American Datum 1983"; } else if( nDatumCode == Datum_WGS84 ) { nEllipsoid = Ellipse_WGS_84; pszName = "World Geodetic System 1984"; } else if( nDatumCode == Datum_WGS72 ) { nEllipsoid = 7043; /* WGS72 */ pszName = "World Geodetic System 1972"; } if (pszName != NULL) { if( pnEllipsoid != NULL ) *pnEllipsoid = (short) nEllipsoid; if( ppszName != NULL ) *ppszName = CPLStrdup( pszName ); return TRUE; } /* -------------------------------------------------------------------- */ /* If we can't find datum.csv then gdal_datum.csv is an */ /* acceptable fallback. Mostly this is for GDAL. */ /* -------------------------------------------------------------------- */ pszFilename = CSVFilename( "datum.csv" ); if( (fp = VSIFOpen(pszFilename,"r")) == NULL ) { if( (fp = VSIFOpen(CSVFilename("gdal_datum.csv"), "r")) != NULL ) { pszFilename = CSVFilename( "gdal_datum.csv" ); VSIFClose( fp ); } } else VSIFClose( fp ); /* -------------------------------------------------------------------- */ /* Search the database for the corresponding datum code. */ /* -------------------------------------------------------------------- */ sprintf( szSearchKey, "%d", nDatumCode ); nEllipsoid = atoi(CSVGetField( pszFilename, "DATUM_CODE", szSearchKey, CC_Integer, "ELLIPSOID_CODE" ) ); if( pnEllipsoid != NULL ) *pnEllipsoid = (short) nEllipsoid; if( nEllipsoid < 1 ) { static int bWarnedOrTried = FALSE; if( !bWarnedOrTried ) { FILE* f = VSIFOpen(CSVFilename( "datum.csv" ), "rb"); if( f == NULL ) f = VSIFOpen(CSVFilename( "gdal_datum.csv" ), "rb"); if( f == NULL ) CPLError(CE_Warning, CPLE_AppDefined, "Cannot find datum.csv or gdal_datum.csv"); else VSIFClose(f); bWarnedOrTried = TRUE; } return FALSE; } /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszName != NULL ) *ppszName = CPLStrdup(CSVGetField( pszFilename, "DATUM_CODE", szSearchKey, CC_Integer, "DATUM_NAME" )); return TRUE; } /************************************************************************/ /* GTIFGetUOMLengthInfo() */ /* */ /* Note: This function should eventually also know how to */ /* lookup length aliases in the UOM_LE_ALIAS table. */ /************************************************************************/ int GTIFGetUOMLengthInfo( int nUOMLengthCode, char **ppszUOMName, double * pdfInMeters ) { char **papszUnitsRecord; char szSearchKey[24]; int iNameField; const char *pszFilename; /* -------------------------------------------------------------------- */ /* We short cut meter to save work and avoid failure for missing */ /* in the most common cases. */ /* -------------------------------------------------------------------- */ if( nUOMLengthCode == 9001 ) { if( ppszUOMName != NULL ) *ppszUOMName = CPLStrdup( "metre" ); if( pdfInMeters != NULL ) *pdfInMeters = 1.0; return TRUE; } if( nUOMLengthCode == 9002 ) { if( ppszUOMName != NULL ) *ppszUOMName = CPLStrdup( "foot" ); if( pdfInMeters != NULL ) *pdfInMeters = 0.3048; return TRUE; } if( nUOMLengthCode == 9003 ) { if( ppszUOMName != NULL ) *ppszUOMName = CPLStrdup( "US survey foot" ); if( pdfInMeters != NULL ) *pdfInMeters = 12.0 / 39.37; return TRUE; } /* -------------------------------------------------------------------- */ /* Search the units database for this unit. If we don't find */ /* it return failure. */ /* -------------------------------------------------------------------- */ pszFilename = CSVFilename( "unit_of_measure.csv" ); sprintf( szSearchKey, "%d", nUOMLengthCode ); papszUnitsRecord = CSVScanFileByName( pszFilename, "UOM_CODE", szSearchKey, CC_Integer ); if( papszUnitsRecord == NULL ) return FALSE; /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszUOMName != NULL ) { iNameField = CSVGetFileFieldId( pszFilename, "UNIT_OF_MEAS_NAME" ); *ppszUOMName = CPLStrdup( CSLGetField(papszUnitsRecord, iNameField) ); } /* -------------------------------------------------------------------- */ /* Get the A and B factor fields, and create the multiplicative */ /* factor. */ /* -------------------------------------------------------------------- */ if( pdfInMeters != NULL ) { int iBFactorField, iCFactorField; iBFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_B" ); iCFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_C" ); if( GTIFAtof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 ) *pdfInMeters = GTIFAtof(CSLGetField(papszUnitsRecord, iBFactorField)) / GTIFAtof(CSLGetField(papszUnitsRecord, iCFactorField)); else *pdfInMeters = 0.0; } return TRUE; } /************************************************************************/ /* GTIFGetUOMAngleInfo() */ /************************************************************************/ int GTIFGetUOMAngleInfo( int nUOMAngleCode, char **ppszUOMName, double * pdfInDegrees ) { const char *pszUOMName = NULL; double dfInDegrees = 1.0; const char *pszFilename; char szSearchKey[24]; switch( nUOMAngleCode ) { case 9101: pszUOMName = "radian"; dfInDegrees = 180.0 / M_PI; break; case 9102: case 9107: case 9108: case 9110: case 9122: pszUOMName = "degree"; dfInDegrees = 1.0; break; case 9103: pszUOMName = "arc-minute"; dfInDegrees = 1 / 60.0; break; case 9104: pszUOMName = "arc-second"; dfInDegrees = 1 / 3600.0; break; case 9105: pszUOMName = "grad"; dfInDegrees = 180.0 / 200.0; break; case 9106: pszUOMName = "gon"; dfInDegrees = 180.0 / 200.0; break; case 9109: pszUOMName = "microradian"; dfInDegrees = 180.0 / (M_PI * 1000000.0); break; default: break; } if (pszUOMName) { if( ppszUOMName != NULL ) { *ppszUOMName = CPLStrdup( pszUOMName ); } if( pdfInDegrees != NULL ) *pdfInDegrees = dfInDegrees; return TRUE; } pszFilename = CSVFilename( "unit_of_measure.csv" ); sprintf( szSearchKey, "%d", nUOMAngleCode ); pszUOMName = CSVGetField( pszFilename, "UOM_CODE", szSearchKey, CC_Integer, "UNIT_OF_MEAS_NAME" ); /* -------------------------------------------------------------------- */ /* If the file is found, read from there. Note that FactorC is */ /* an empty field for any of the DMS style formats, and in this */ /* case we really want to return the default InDegrees value */ /* (1.0) from above. */ /* -------------------------------------------------------------------- */ if( pszUOMName != NULL ) { double dfFactorB, dfFactorC, dfInRadians; dfFactorB = GTIFAtof(CSVGetField( pszFilename, "UOM_CODE", szSearchKey, CC_Integer, "FACTOR_B" )); dfFactorC = GTIFAtof(CSVGetField( pszFilename, "UOM_CODE", szSearchKey, CC_Integer, "FACTOR_C" )); if( dfFactorC != 0.0 ) { dfInRadians = (dfFactorB / dfFactorC); dfInDegrees = dfInRadians * 180.0 / M_PI; } if( ppszUOMName != NULL ) *ppszUOMName = CPLStrdup( pszUOMName ); } else { return FALSE; } /* -------------------------------------------------------------------- */ /* Return to caller. */ /* -------------------------------------------------------------------- */ if( pdfInDegrees != NULL ) *pdfInDegrees = dfInDegrees; return TRUE; } /************************************************************************/ /* EPSGProjMethodToCTProjMethod() */ /* */ /* Convert between the EPSG enumeration for projection methods, */ /* and the GeoTIFF CT codes. */ /************************************************************************/ static int EPSGProjMethodToCTProjMethod( int nEPSG, int bReturnExtendedCTCode ) { /* see trf_method.csv for list of EPSG codes */ switch( nEPSG ) { case 9801: return CT_LambertConfConic_1SP; case 9802: return CT_LambertConfConic_2SP; case 9803: return CT_LambertConfConic_2SP; /* Belgian variant not supported */ case 9804: return CT_Mercator; /* 1SP and 2SP not differentiated */ case 9805: if( bReturnExtendedCTCode ) return CT_Ext_Mercator_2SP; else return CT_Mercator; /* 1SP and 2SP not differentiated */ /* Mercator 1SP (Spherical) For EPSG:3785 */ case 9841: return CT_Mercator; /* 1SP and 2SP not differentiated */ /* Google Mercator For EPSG:3857 */ case 1024: return CT_Mercator; /* 1SP and 2SP not differentiated */ case 9806: return CT_CassiniSoldner; case 9807: return CT_TransverseMercator; case 9808: return CT_TransvMercator_SouthOriented; case 9809: return CT_ObliqueStereographic; case 9810: case 9829: /* variant B not quite the same - not sure how to handle */ return CT_PolarStereographic; case 9811: return CT_NewZealandMapGrid; case 9812: return CT_ObliqueMercator; /* is hotine actually different? */ case 9813: return CT_ObliqueMercator_Laborde; case 9814: return CT_ObliqueMercator_Rosenmund; /* swiss */ case 9815: return CT_HotineObliqueMercatorAzimuthCenter; case 9816: /* tunesia mining grid has no counterpart */ return KvUserDefined; case 9818: return CT_Polyconic; case 9820: case 1027: return CT_LambertAzimEqualArea; case 9822: return CT_AlbersEqualArea; case 9834: return CT_CylindricalEqualArea; case 1028: case 1029: case 9823: /* spherical */ case 9842: /* elliptical */ return CT_Equirectangular; default: /* use the EPSG code for other methods */ return nEPSG; } } /************************************************************************/ /* SetGTParmIds() */ /* */ /* This is hardcoded logic to set the GeoTIFF parameter */ /* identifiers for all the EPSG supported projections. As the */ /* trf_method.csv table grows with new projections, this code */ /* will need to be updated. */ /************************************************************************/ static int SetGTParmIds( int nCTProjection, int *panProjParmId, int *panEPSGCodes ) { int anWorkingDummy[7]; if( panEPSGCodes == NULL ) panEPSGCodes = anWorkingDummy; if( panProjParmId == NULL ) panProjParmId = anWorkingDummy; memset( panEPSGCodes, 0, sizeof(int) * 7 ); /* psDefn->nParms = 7; */ switch( nCTProjection ) { case CT_CassiniSoldner: case CT_NewZealandMapGrid: case CT_Polyconic: panProjParmId[0] = ProjNatOriginLatGeoKey; panProjParmId[1] = ProjNatOriginLongGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGNatOriginLat; panEPSGCodes[1] = EPSGNatOriginLong; panEPSGCodes[5] = EPSGFalseEasting; panEPSGCodes[6] = EPSGFalseNorthing; return TRUE; case CT_ObliqueMercator: case CT_HotineObliqueMercatorAzimuthCenter: panProjParmId[0] = ProjCenterLatGeoKey; panProjParmId[1] = ProjCenterLongGeoKey; panProjParmId[2] = ProjAzimuthAngleGeoKey; panProjParmId[3] = ProjRectifiedGridAngleGeoKey; panProjParmId[4] = ProjScaleAtCenterGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGProjCenterLat; panEPSGCodes[1] = EPSGProjCenterLong; panEPSGCodes[2] = EPSGAzimuth; panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid; panEPSGCodes[4] = EPSGInitialLineScaleFactor; panEPSGCodes[5] = EPSGProjCenterEasting; /* EPSG proj method 9812 uses EPSGFalseEasting, but 9815 uses EPSGProjCenterEasting */ panEPSGCodes[6] = EPSGProjCenterNorthing; /* EPSG proj method 9812 uses EPSGFalseNorthing, but 9815 uses EPSGProjCenterNorthing */ return TRUE; case CT_ObliqueMercator_Laborde: panProjParmId[0] = ProjCenterLatGeoKey; panProjParmId[1] = ProjCenterLongGeoKey; panProjParmId[2] = ProjAzimuthAngleGeoKey; panProjParmId[4] = ProjScaleAtCenterGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGProjCenterLat; panEPSGCodes[1] = EPSGProjCenterLong; panEPSGCodes[2] = EPSGAzimuth; panEPSGCodes[4] = EPSGInitialLineScaleFactor; panEPSGCodes[5] = EPSGProjCenterEasting; panEPSGCodes[6] = EPSGProjCenterNorthing; return TRUE; case CT_LambertConfConic_1SP: case CT_Mercator: case CT_ObliqueStereographic: case CT_PolarStereographic: case CT_TransverseMercator: case CT_TransvMercator_SouthOriented: panProjParmId[0] = ProjNatOriginLatGeoKey; panProjParmId[1] = ProjNatOriginLongGeoKey; panProjParmId[4] = ProjScaleAtNatOriginGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGNatOriginLat; panEPSGCodes[1] = EPSGNatOriginLong; panEPSGCodes[4] = EPSGNatOriginScaleFactor; panEPSGCodes[5] = EPSGFalseEasting; panEPSGCodes[6] = EPSGFalseNorthing; return TRUE; case CT_LambertConfConic_2SP: panProjParmId[0] = ProjFalseOriginLatGeoKey; panProjParmId[1] = ProjFalseOriginLongGeoKey; panProjParmId[2] = ProjStdParallel1GeoKey; panProjParmId[3] = ProjStdParallel2GeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGFalseOriginLat; panEPSGCodes[1] = EPSGFalseOriginLong; panEPSGCodes[2] = EPSGStdParallel1Lat; panEPSGCodes[3] = EPSGStdParallel2Lat; panEPSGCodes[5] = EPSGFalseOriginEasting; panEPSGCodes[6] = EPSGFalseOriginNorthing; return TRUE; case CT_AlbersEqualArea: panProjParmId[0] = ProjStdParallel1GeoKey; panProjParmId[1] = ProjStdParallel2GeoKey; panProjParmId[2] = ProjNatOriginLatGeoKey; panProjParmId[3] = ProjNatOriginLongGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGStdParallel1Lat; panEPSGCodes[1] = EPSGStdParallel2Lat; panEPSGCodes[2] = EPSGFalseOriginLat; panEPSGCodes[3] = EPSGFalseOriginLong; panEPSGCodes[5] = EPSGFalseOriginEasting; panEPSGCodes[6] = EPSGFalseOriginNorthing; return TRUE; case CT_SwissObliqueCylindrical: panProjParmId[0] = ProjCenterLatGeoKey; panProjParmId[1] = ProjCenterLongGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; /* EPSG codes? */ return TRUE; case CT_LambertAzimEqualArea: panProjParmId[0] = ProjCenterLatGeoKey; panProjParmId[1] = ProjCenterLongGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGNatOriginLat; panEPSGCodes[1] = EPSGNatOriginLong; panEPSGCodes[5] = EPSGFalseEasting; panEPSGCodes[6] = EPSGFalseNorthing; return TRUE; case CT_CylindricalEqualArea: panProjParmId[0] = ProjStdParallel1GeoKey; panProjParmId[1] = ProjNatOriginLongGeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGStdParallel1Lat; panEPSGCodes[1] = EPSGFalseOriginLong; panEPSGCodes[5] = EPSGFalseOriginEasting; panEPSGCodes[6] = EPSGFalseOriginNorthing; return TRUE; case CT_Equirectangular: panProjParmId[0] = ProjCenterLatGeoKey; panProjParmId[1] = ProjCenterLongGeoKey; panProjParmId[2] = ProjStdParallel1GeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGNatOriginLat; panEPSGCodes[1] = EPSGNatOriginLong; panEPSGCodes[2] = EPSGStdParallel1Lat; panEPSGCodes[5] = EPSGFalseEasting; panEPSGCodes[6] = EPSGFalseNorthing; return TRUE; case CT_Ext_Mercator_2SP: panProjParmId[0] = ProjNatOriginLatGeoKey; panProjParmId[1] = ProjNatOriginLongGeoKey; panProjParmId[2] = ProjStdParallel1GeoKey; panProjParmId[5] = ProjFalseEastingGeoKey; panProjParmId[6] = ProjFalseNorthingGeoKey; panEPSGCodes[0] = EPSGNatOriginLat; panEPSGCodes[1] = EPSGNatOriginLong; panEPSGCodes[2] = EPSGStdParallel1Lat; panEPSGCodes[5] = EPSGFalseEasting; panEPSGCodes[6] = EPSGFalseNorthing; return TRUE; default: return FALSE; } } /************************************************************************/ /* GTIFGetProjTRFInfo() */ /* */ /* Transform a PROJECTION_TRF_CODE into a projection method, */ /* and a set of parameters. The parameters identify will */ /* depend on the returned method, but they will all have been */ /* normalized into degrees and meters. */ /************************************************************************/ int GTIFGetProjTRFInfo( /* COORD_OP_CODE from coordinate_operation.csv */ int nProjTRFCode, char **ppszProjTRFName, short * pnProjMethod, double * padfProjParms ) { int nProjMethod, i, anEPSGCodes[7]; double adfProjParms[7]; char szTRFCode[16]; int nCTProjMethod; char *pszFilename; if ((nProjTRFCode >= Proj_UTM_zone_1N && nProjTRFCode <= Proj_UTM_zone_60N) || (nProjTRFCode >= Proj_UTM_zone_1S && nProjTRFCode <= Proj_UTM_zone_60S)) { int bNorth; int nZone; if (nProjTRFCode <= Proj_UTM_zone_60N) { bNorth = TRUE; nZone = nProjTRFCode - Proj_UTM_zone_1N + 1; } else { bNorth = FALSE; nZone = nProjTRFCode - Proj_UTM_zone_1S + 1; } if (ppszProjTRFName) { char szProjTRFName[64]; sprintf(szProjTRFName, "UTM zone %d%c", nZone, (bNorth) ? 'N' : 'S'); *ppszProjTRFName = CPLStrdup(szProjTRFName); } if (pnProjMethod) *pnProjMethod = 9807; if (padfProjParms) { padfProjParms[0] = 0; padfProjParms[1] = -183 + 6 * nZone; padfProjParms[2] = 0; padfProjParms[3] = 0; padfProjParms[4] = 0.9996; padfProjParms[5] = 500000; padfProjParms[6] = (bNorth) ? 0 : 10000000; } return TRUE; } /* -------------------------------------------------------------------- */ /* Get the proj method. If this fails to return a meaningful */ /* number, then the whole function fails. */ /* -------------------------------------------------------------------- */ pszFilename = CPLStrdup(CSVFilename("projop_wparm.csv")); sprintf( szTRFCode, "%d", nProjTRFCode ); nProjMethod = atoi( CSVGetField( pszFilename, "COORD_OP_CODE", szTRFCode, CC_Integer, "COORD_OP_METHOD_CODE" ) ); if( nProjMethod == 0 ) { CPLFree( pszFilename ); return FALSE; } /* -------------------------------------------------------------------- */ /* Initialize a definition of what EPSG codes need to be loaded */ /* into what fields in adfProjParms. */ /* -------------------------------------------------------------------- */ nCTProjMethod = EPSGProjMethodToCTProjMethod( nProjMethod, TRUE ); SetGTParmIds( nCTProjMethod, NULL, anEPSGCodes ); /* -------------------------------------------------------------------- */ /* Get the parameters for this projection. For the time being */ /* I am assuming the first four parameters are angles, the */ /* fifth is unitless (normally scale), and the remainder are */ /* linear measures. This works fine for the existing */ /* projections, but is a pretty fragile approach. */ /* -------------------------------------------------------------------- */ for( i = 0; i < 7; i++ ) { char szParamUOMID[32], szParamValueID[32], szParamCodeID[32]; const char *pszValue; int nUOM; int nEPSGCode = anEPSGCodes[i]; int iEPSG; /* Establish default */ if( nEPSGCode == EPSGAngleRectifiedToSkewedGrid ) adfProjParms[i] = 90.0; else if( nEPSGCode == EPSGNatOriginScaleFactor || nEPSGCode == EPSGInitialLineScaleFactor || nEPSGCode == EPSGPseudoStdParallelScaleFactor ) adfProjParms[i] = 1.0; else adfProjParms[i] = 0.0; /* If there is no parameter, skip */ if( nEPSGCode == 0 ) continue; /* Find the matching parameter */ for( iEPSG = 0; iEPSG < 7; iEPSG++ ) { sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 ); if( atoi(CSVGetField( pszFilename, "COORD_OP_CODE", szTRFCode, CC_Integer, szParamCodeID )) == nEPSGCode ) break; } /* not found, accept the default */ if( iEPSG == 7 ) { /* for CT_ObliqueMercator try alternate parameter codes first */ /* because EPSG proj method 9812 uses EPSGFalseXXXXX, but 9815 uses EPSGProjCenterXXXXX */ if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterEasting ) nEPSGCode = EPSGFalseEasting; else if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterNorthing ) nEPSGCode = EPSGFalseNorthing; /* for CT_PolarStereographic try alternate parameter codes first */ /* because EPSG proj method 9829 uses EPSGLatOfStdParallel instead of EPSGNatOriginLat */ /* and EPSGOriginLong instead of EPSGNatOriginLong */ else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLat ) nEPSGCode = EPSGLatOfStdParallel; else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLong ) nEPSGCode = EPSGOriginLong; else continue; for( iEPSG = 0; iEPSG < 7; iEPSG++ ) { sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 ); if( atoi(CSVGetField( pszFilename, "COORD_OP_CODE", szTRFCode, CC_Integer, szParamCodeID )) == nEPSGCode ) break; } if( iEPSG == 7 ) continue; } /* Get the value, and UOM */ sprintf( szParamUOMID, "PARAMETER_UOM_%d", iEPSG+1 ); sprintf( szParamValueID, "PARAMETER_VALUE_%d", iEPSG+1 ); nUOM = atoi(CSVGetField( pszFilename, "COORD_OP_CODE", szTRFCode, CC_Integer, szParamUOMID )); pszValue = CSVGetField( pszFilename, "COORD_OP_CODE", szTRFCode, CC_Integer, szParamValueID ); /* Transform according to the UOM */ if( nUOM >= 9100 && nUOM < 9200 ) adfProjParms[i] = GTIFAngleStringToDD( pszValue, nUOM ); else if( nUOM > 9000 && nUOM < 9100 ) { double dfInMeters; if( !GTIFGetUOMLengthInfo( nUOM, NULL, &dfInMeters ) ) dfInMeters = 1.0; adfProjParms[i] = GTIFAtof(pszValue) * dfInMeters; } else adfProjParms[i] = GTIFAtof(pszValue); } /* -------------------------------------------------------------------- */ /* Get the name, if requested. */ /* -------------------------------------------------------------------- */ if( ppszProjTRFName != NULL ) { *ppszProjTRFName = CPLStrdup(CSVGetField( pszFilename, "COORD_OP_CODE", szTRFCode, CC_Integer, "COORD_OP_NAME" )); } /* -------------------------------------------------------------------- */ /* Transfer requested data into passed variables. */ /* -------------------------------------------------------------------- */ if( pnProjMethod != NULL ) *pnProjMethod = (short) nProjMethod; if( padfProjParms != NULL ) { for( i = 0; i < 7; i++ ) padfProjParms[i] = adfProjParms[i]; } CPLFree( pszFilename ); return TRUE; } /************************************************************************/ /* GTIFKeyGetInternal() */ /************************************************************************/ static int GTIFKeyGetInternal( GTIF *psGTIF, geokey_t key, void* pData, int nIndex, int nCount, tagtype_t expected_tagtype ) { tagtype_t tagtype; if( !GTIFKeyInfo(psGTIF, key, NULL, &tagtype) ) return 0; if( tagtype != expected_tagtype ) { static int nErrorCount = 0; if( ++nErrorCount < 100 ) { fprintf(stderr, "Expected key %s to be of type %s. Got %s\n", GTIFKeyName(key), GTIFTypeName(expected_tagtype), GTIFTypeName(tagtype)); } return 0; } return GTIFKeyGet( psGTIF, key, pData, nIndex, nCount ); } /************************************************************************/ /* GTIFKeyGetSHORT() */ /************************************************************************/ static int GTIFKeyGetSHORT( GTIF *psGTIF, geokey_t key, short* pnVal, int nIndex, int nCount ) { return GTIFKeyGetInternal(psGTIF, key, pnVal, nIndex, nCount, TYPE_SHORT); } /************************************************************************/ /* GDALGTIFKeyGetDOUBLE() */ /************************************************************************/ static int GTIFKeyGetDOUBLE( GTIF *psGTIF, geokey_t key, double* pdfVal, int nIndex, int nCount ) { return GTIFKeyGetInternal(psGTIF, key, pdfVal, nIndex, nCount, TYPE_DOUBLE); } /************************************************************************/ /* GTIFFetchProjParms() */ /* */ /* Fetch the projection parameters for a particular projection */ /* from a GeoTIFF file, and fill the GTIFDefn structure out */ /* with them. */ /************************************************************************/ static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn ) { double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0; double dfFalseEasting = 0.0, dfFalseNorthing = 0.0, dfNatOriginScale = 1.0; double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0; int iParm; int bHaveSP1, bHaveNOS; /* -------------------------------------------------------------------- */ /* Get the false easting, and northing if available. */ /* -------------------------------------------------------------------- */ if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFalseEasting, 0, 1) && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterEastingGeoKey, &dfFalseEasting, 0, 1) && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey, &dfFalseEasting, 0, 1) ) dfFalseEasting = 0.0; if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFalseNorthing,0,1) && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterNorthingGeoKey, &dfFalseNorthing, 0, 1) && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey, &dfFalseNorthing, 0, 1) ) dfFalseNorthing = 0.0; switch( psDefn->CTProjection ) { /* -------------------------------------------------------------------- */ case CT_Stereographic: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, &dfNatOriginScale, 0, 1 ) == 0 ) dfNatOriginScale = 1.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjCenterLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjCenterLongGeoKey; psDefn->ProjParm[4] = dfNatOriginScale; psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_Mercator: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; bHaveSP1 = GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, &dfStdParallel1, 0, 1 ); bHaveNOS = GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, &dfNatOriginScale, 0, 1 ); /* Default scale only if dfStdParallel1 isn't defined either */ if( !bHaveNOS && !bHaveSP1) { bHaveNOS = TRUE; dfNatOriginScale = 1.0; } /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; if( bHaveSP1 ) { psDefn->ProjParm[2] = dfStdParallel1; psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; } if( bHaveNOS ) { psDefn->ProjParm[4] = dfNatOriginScale; psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; } psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_LambertConfConic_1SP: case CT_ObliqueStereographic: case CT_TransverseMercator: case CT_TransvMercator_SouthOriented: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, &dfNatOriginScale, 0, 1 ) == 0 ) dfNatOriginScale = 1.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; psDefn->ProjParm[4] = dfNatOriginScale; psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_ObliqueMercator: /* hotine */ case CT_HotineObliqueMercatorAzimuthCenter: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjAzimuthAngleGeoKey, &dfAzimuth, 0, 1 ) == 0 ) dfAzimuth = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjRectifiedGridAngleGeoKey, &dfRectGridAngle, 0, 1 ) == 0 ) dfRectGridAngle = 90.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, &dfNatOriginScale, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey, &dfNatOriginScale, 0, 1 ) == 0 ) dfNatOriginScale = 1.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjCenterLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjCenterLongGeoKey; psDefn->ProjParm[2] = dfAzimuth; psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey; psDefn->ProjParm[3] = dfRectGridAngle; psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey; psDefn->ProjParm[4] = dfNatOriginScale; psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_CassiniSoldner: case CT_Polyconic: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, &dfNatOriginScale, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey, &dfNatOriginScale, 0, 1 ) == 0 ) dfNatOriginScale = 1.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; psDefn->ProjParm[4] = dfNatOriginScale; psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_AzimuthalEquidistant: case CT_MillerCylindrical: case CT_Gnomonic: case CT_LambertAzimEqualArea: case CT_Orthographic: case CT_NewZealandMapGrid: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjCenterLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjCenterLongGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_Equirectangular: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, &dfStdParallel1, 0, 1 ) == 0 ) dfStdParallel1 = 0.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjCenterLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjCenterLongGeoKey; psDefn->ProjParm[2] = dfStdParallel1; psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_Robinson: case CT_Sinusoidal: case CT_VanDerGrinten: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjCenterLongGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_PolarStereographic: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjStraightVertPoleLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, &dfNatOriginScale, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey, &dfNatOriginScale, 0, 1 ) == 0 ) dfNatOriginScale = 1.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey; psDefn->ProjParm[4] = dfNatOriginScale; psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_LambertConfConic_2SP: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, &dfStdParallel1, 0, 1 ) == 0 ) dfStdParallel1 = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey, &dfStdParallel2, 0, 1 ) == 0 ) dfStdParallel1 = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfNatOriginLat; psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey; psDefn->ProjParm[2] = dfStdParallel1; psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; psDefn->ProjParm[3] = dfStdParallel2; psDefn->ProjParmId[3] = ProjStdParallel2GeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_AlbersEqualArea: case CT_EquidistantConic: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, &dfStdParallel1, 0, 1 ) == 0 ) dfStdParallel1 = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey, &dfStdParallel2, 0, 1 ) == 0 ) dfStdParallel2 = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, &dfNatOriginLat, 0, 1 ) == 0 ) dfNatOriginLat = 0.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfStdParallel1; psDefn->ProjParmId[0] = ProjStdParallel1GeoKey; psDefn->ProjParm[1] = dfStdParallel2; psDefn->ProjParmId[1] = ProjStdParallel2GeoKey; psDefn->ProjParm[2] = dfNatOriginLat; psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey; psDefn->ProjParm[3] = dfNatOriginLong; psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; /* -------------------------------------------------------------------- */ case CT_CylindricalEqualArea: /* -------------------------------------------------------------------- */ if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, &dfStdParallel1, 0, 1 ) == 0 ) dfStdParallel1 = 0.0; if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, &dfNatOriginLong, 0, 1 ) == 0 ) dfNatOriginLong = 0.0; /* notdef: should transform to decimal degrees at this point */ psDefn->ProjParm[0] = dfStdParallel1; psDefn->ProjParmId[0] = ProjStdParallel1GeoKey; psDefn->ProjParm[1] = dfNatOriginLong; psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; psDefn->ProjParm[5] = dfFalseEasting; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[6] = dfFalseNorthing; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; psDefn->nParms = 7; break; } /* -------------------------------------------------------------------- */ /* Normalize any linear parameters into meters. In GeoTIFF */ /* the linear projection parameter tags are normally in the */ /* units of the coordinate system described. */ /* -------------------------------------------------------------------- */ for( iParm = 0; iParm < psDefn->nParms; iParm++ ) { switch( psDefn->ProjParmId[iParm] ) { case ProjFalseEastingGeoKey: case ProjFalseNorthingGeoKey: case ProjFalseOriginEastingGeoKey: case ProjFalseOriginNorthingGeoKey: case ProjCenterEastingGeoKey: case ProjCenterNorthingGeoKey: if( psDefn->UOMLengthInMeters != 0 && psDefn->UOMLengthInMeters != 1.0 ) { psDefn->ProjParm[iParm] *= psDefn->UOMLengthInMeters; } break; default: break; } } } /************************************************************************/ /* GTIFGetDefn() */ /************************************************************************/ /** @param psGTIF GeoTIFF information handle as returned by GTIFNew. @param psDefn Pointer to an existing GTIFDefn structure allocated by GTIFAllocDefn(). @return TRUE if the function has been successful, otherwise FALSE. This function reads the coordinate system definition from a GeoTIFF file, and <i>normalizes</i> it into a set of component information using definitions from CSV (Comma Separated Value ASCII) files derived from EPSG tables. This function is intended to simplify correct support for reading files with defined PCS (Projected Coordinate System) codes that wouldn't otherwise be directly known by application software by reducing it to the underlying projection method, parameters, datum, ellipsoid, prime meridian and units.<p> The application should pass a pointer to an existing uninitialized GTIFDefn structure, and GTIFGetDefn() will fill it in. The function currently always returns TRUE but in the future will return FALSE if CSV files are not found. In any event, all geokeys actually found in the file will be copied into the GTIFDefn. However, if the CSV files aren't found codes implied by other codes will not be set properly.<p> GTIFGetDefn() will not generally work if the EPSG derived CSV files cannot be found. By default a modest attempt will be made to find them, but in general it is necessary for the calling application to override the logic to find them. This can be done by calling the SetCSVFilenameHook() function to override the search method based on application knowledge of where they are found.<p> The normalization methodology operates by fetching tags from the GeoTIFF file, and then setting all other tags implied by them in the structure. The implied relationships are worked out by reading definitions from the various EPSG derived CSV tables.<p> For instance, if a PCS (ProjectedCSTypeGeoKey) is found in the GeoTIFF file this code is used to lookup a record in the <tt>horiz_cs.csv</tt> CSV file. For example given the PCS 26746 we can find the name (NAD27 / California zone VI), the GCS 4257 (NAD27), and the ProjectionCode 10406 (California CS27 zone VI). The GCS, and ProjectionCode can in turn be looked up in other tables until all the details of units, ellipsoid, prime meridian, datum, projection (LambertConfConic_2SP) and projection parameters are established. A full listgeo dump of a file for this result might look like the following, all based on a single PCS value:<p> <pre> % listgeo -norm ~/data/geotiff/pci_eg/spaf27.tif Geotiff_Information: Version: 1 Key_Revision: 1.0 Tagged_Information: ModelTiepointTag (2,3): 0 0 0 1577139.71 634349.176 0 ModelPixelScaleTag (1,3): 195.509321 198.32184 0 End_Of_Tags. Keyed_Information: GTModelTypeGeoKey (Short,1): ModelTypeProjected GTRasterTypeGeoKey (Short,1): RasterPixelIsArea ProjectedCSTypeGeoKey (Short,1): PCS_NAD27_California_VI End_Of_Keys. End_Of_Geotiff. PCS = 26746 (NAD27 / California zone VI) Projection = 10406 (California CS27 zone VI) Projection Method: CT_LambertConfConic_2SP ProjStdParallel1GeoKey: 33.883333 ProjStdParallel2GeoKey: 32.766667 ProjFalseOriginLatGeoKey: 32.166667 ProjFalseOriginLongGeoKey: -116.233333 ProjFalseEastingGeoKey: 609601.219202 ProjFalseNorthingGeoKey: 0.000000 GCS: 4267/NAD27 Datum: 6267/North American Datum 1927 Ellipsoid: 7008/Clarke 1866 (6378206.40,6356583.80) Prime Meridian: 8901/Greenwich (0.000000) Projection Linear Units: 9003/US survey foot (0.304801m) </pre> Note that GTIFGetDefn() does not inspect or return the tiepoints and scale. This must be handled separately as it normally would. It is intended to simplify capture and normalization of the coordinate system definition. Note that GTIFGetDefn() also does the following things: <ol> <li> Convert all angular values to decimal degrees. <li> Convert all linear values to meters. <li> Return the linear units and conversion to meters for the tiepoints and scale (though the tiepoints and scale remain in their native units). <li> When reading projection parameters a variety of differences between different GeoTIFF generators are handled, and a normalized set of parameters for each projection are always returned. </ol> Code fields in the GTIFDefn are filled with KvUserDefined if there is not value to assign. The parameter lists for each of the underlying projection transform methods can be found at the <a href="http://www.remotesensing.org/geotiff/proj_list">Projections</a> page. Note that nParms will be set based on the maximum parameter used. Some of the parameters may not be used in which case the GTIFDefn::ProjParmId[] will be zero. This is done to retain correspondence to the EPSG parameter numbering scheme.<p> The <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 be used as an example of code that converts a GTIFDefn into another projection system.<p> @see GTIFKeySet(), SetCSVFilenameHook() */ int GTIFGetDefn( GTIF * psGTIF, GTIFDefn * psDefn ) { int i; short nGeogUOMLinear; double dfInvFlattening; /* -------------------------------------------------------------------- */ /* Initially we default all the information we can. */ /* -------------------------------------------------------------------- */ psDefn->DefnSet = 1; psDefn->Model = KvUserDefined; psDefn->PCS = KvUserDefined; psDefn->GCS = KvUserDefined; psDefn->UOMLength = KvUserDefined; psDefn->UOMLengthInMeters = 1.0; psDefn->UOMAngle = KvUserDefined; psDefn->UOMAngleInDegrees = 1.0; psDefn->Datum = KvUserDefined; psDefn->Ellipsoid = KvUserDefined; psDefn->SemiMajor = 0.0; psDefn->SemiMinor = 0.0; psDefn->PM = KvUserDefined; psDefn->PMLongToGreenwich = 0.0; #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) psDefn->TOWGS84Count = 0; memset( psDefn->TOWGS84, 0, sizeof(psDefn->TOWGS84) ); #endif psDefn->ProjCode = KvUserDefined; psDefn->Projection = KvUserDefined; psDefn->CTProjection = KvUserDefined; psDefn->nParms = 0; for( i = 0; i < MAX_GTIF_PROJPARMS; i++ ) { psDefn->ProjParm[i] = 0.0; psDefn->ProjParmId[i] = 0; } psDefn->MapSys = KvUserDefined; psDefn->Zone = 0; /* -------------------------------------------------------------------- */ /* Do we have any geokeys? */ /* -------------------------------------------------------------------- */ { int nKeyCount = 0; int anVersion[3]; GTIFDirectoryInfo( psGTIF, anVersion, &nKeyCount ); if( nKeyCount == 0 ) { psDefn->DefnSet = 0; return FALSE; } } /* -------------------------------------------------------------------- */ /* Try to get the overall model type. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF,GTModelTypeGeoKey,&(psDefn->Model),0,1); /* -------------------------------------------------------------------- */ /* Extract the Geog units. */ /* -------------------------------------------------------------------- */ nGeogUOMLinear = 9001; /* Linear_Meter */ GTIFKeyGetSHORT(psGTIF, GeogLinearUnitsGeoKey, &nGeogUOMLinear, 0, 1 ); /* -------------------------------------------------------------------- */ /* Try to get a PCS. */ /* -------------------------------------------------------------------- */ if( GTIFKeyGetSHORT(psGTIF,ProjectedCSTypeGeoKey, &(psDefn->PCS),0,1) == 1 && psDefn->PCS != KvUserDefined ) { /* * Translate this into useful information. */ GTIFGetPCSInfo( psDefn->PCS, NULL, &(psDefn->ProjCode), &(psDefn->UOMLength), &(psDefn->GCS) ); } /* -------------------------------------------------------------------- */ /* If we have the PCS code, but didn't find it in the CSV files */ /* (likely because we can't find them) we will try some ``jiffy */ /* rules'' for UTM and state plane. */ /* -------------------------------------------------------------------- */ if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined ) { int nMapSys, nZone; int nGCS = psDefn->GCS; nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone ); if( nMapSys != KvUserDefined ) { psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone ); psDefn->GCS = (short) nGCS; } } /* -------------------------------------------------------------------- */ /* If the Proj_ code is specified directly, use that. */ /* -------------------------------------------------------------------- */ if( psDefn->ProjCode == KvUserDefined ) GTIFKeyGetSHORT(psGTIF, ProjectionGeoKey, &(psDefn->ProjCode), 0, 1 ); if( psDefn->ProjCode != KvUserDefined ) { /* * We have an underlying projection transformation value. Look * this up. For a PCS of ``WGS 84 / UTM 11'' the transformation * would be Transverse Mercator, with a particular set of options. * The nProjTRFCode itself would correspond to the name * ``UTM zone 11N'', and doesn't include datum info. */ GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection), psDefn->ProjParm ); /* * Set the GeoTIFF identity of the parameters. */ psDefn->CTProjection = (short) EPSGProjMethodToCTProjMethod( psDefn->Projection, FALSE ); SetGTParmIds( EPSGProjMethodToCTProjMethod(psDefn->Projection, TRUE), psDefn->ProjParmId, NULL); psDefn->nParms = 7; } /* -------------------------------------------------------------------- */ /* Try to get a GCS. If found, it will override any implied by */ /* the PCS. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF, GeographicTypeGeoKey, &(psDefn->GCS), 0, 1 ); if( psDefn->GCS < 1 || psDefn->GCS >= KvUserDefined ) psDefn->GCS = KvUserDefined; /* -------------------------------------------------------------------- */ /* Derive the datum, and prime meridian from the GCS. */ /* -------------------------------------------------------------------- */ if( psDefn->GCS != KvUserDefined ) { GTIFGetGCSInfo( psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM), &(psDefn->UOMAngle) ); } /* -------------------------------------------------------------------- */ /* Handle the GCS angular units. GeogAngularUnitsGeoKey */ /* overrides the GCS or PCS setting. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF, GeogAngularUnitsGeoKey, &(psDefn->UOMAngle), 0, 1 ); if( psDefn->UOMAngle != KvUserDefined ) { GTIFGetUOMAngleInfo( psDefn->UOMAngle, NULL, &(psDefn->UOMAngleInDegrees) ); } /* -------------------------------------------------------------------- */ /* Check for a datum setting, and then use the datum to derive */ /* an ellipsoid. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF, GeogGeodeticDatumGeoKey, &(psDefn->Datum), 0, 1 ); if( psDefn->Datum != KvUserDefined ) { GTIFGetDatumInfo( psDefn->Datum, NULL, &(psDefn->Ellipsoid) ); } /* -------------------------------------------------------------------- */ /* Check for an explicit ellipsoid. Use the ellipsoid to */ /* derive the ellipsoid characteristics, if possible. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF, GeogEllipsoidGeoKey, &(psDefn->Ellipsoid), 0, 1 ); if( psDefn->Ellipsoid != KvUserDefined ) { GTIFGetEllipsoidInfo( psDefn->Ellipsoid, NULL, &(psDefn->SemiMajor), &(psDefn->SemiMinor) ); } /* -------------------------------------------------------------------- */ /* Check for overridden ellipsoid parameters. It would be nice */ /* to warn if they conflict with provided information, but for */ /* now we just override. */ /* -------------------------------------------------------------------- */ CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 )); CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogSemiMinorAxisGeoKey, &(psDefn->SemiMinor), 0, 1 )); if( GTIFKeyGetDOUBLE(psGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening, 0, 1 ) == 1 ) { if( dfInvFlattening != 0.0 ) psDefn->SemiMinor = psDefn->SemiMajor * (1 - 1.0/dfInvFlattening); else psDefn->SemiMinor = psDefn->SemiMajor; } /* -------------------------------------------------------------------- */ /* Get the prime meridian info. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF, GeogPrimeMeridianGeoKey, &(psDefn->PM), 0, 1 ); if( psDefn->PM != KvUserDefined ) { GTIFGetPMInfo( psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) ); } else { CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogPrimeMeridianLongGeoKey, &(psDefn->PMLongToGreenwich), 0, 1 )); psDefn->PMLongToGreenwich = GTIFAngleToDD( psDefn->PMLongToGreenwich, psDefn->UOMAngle ); } /* -------------------------------------------------------------------- */ /* Get the TOWGS84 parameters. */ /* -------------------------------------------------------------------- */ #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) psDefn->TOWGS84Count = (short)GTIFKeyGetDOUBLE(psGTIF, GeogTOWGS84GeoKey, psDefn->TOWGS84, 0, 7 ); #endif /* -------------------------------------------------------------------- */ /* Have the projection units of measure been overridden? We */ /* should likely be doing something about angular units too, */ /* but these are very rarely not decimal degrees for actual */ /* file coordinates. */ /* -------------------------------------------------------------------- */ GTIFKeyGetSHORT(psGTIF,ProjLinearUnitsGeoKey,&(psDefn->UOMLength),0,1); if( psDefn->UOMLength != KvUserDefined ) { GTIFGetUOMLengthInfo( psDefn->UOMLength, NULL, &(psDefn->UOMLengthInMeters) ); } else { CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF,ProjLinearUnitSizeGeoKey,&(psDefn->UOMLengthInMeters),0,1)); } /* -------------------------------------------------------------------- */ /* Handle a variety of user defined transform types. */ /* -------------------------------------------------------------------- */ if( GTIFKeyGetSHORT(psGTIF,ProjCoordTransGeoKey, &(psDefn->CTProjection),0,1) == 1) { GTIFFetchProjParms( psGTIF, psDefn ); } /* -------------------------------------------------------------------- */ /* Try to set the zoned map system information. */ /* -------------------------------------------------------------------- */ psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) ); /* -------------------------------------------------------------------- */ /* If this is UTM, and we were unable to extract the projection */ /* parameters from the CSV file, just set them directly now, */ /* since it's pretty easy, and a common case. */ /* -------------------------------------------------------------------- */ if( (psDefn->MapSys == MapSys_UTM_North || psDefn->MapSys == MapSys_UTM_South) && psDefn->CTProjection == KvUserDefined ) { psDefn->CTProjection = CT_TransverseMercator; psDefn->nParms = 7; psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; psDefn->ProjParm[0] = 0.0; psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0; psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; psDefn->ProjParm[4] = 0.9996; psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; psDefn->ProjParm[5] = 500000.0; psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; if( psDefn->MapSys == MapSys_UTM_North ) psDefn->ProjParm[6] = 0.0; else psDefn->ProjParm[6] = 10000000.0; } return TRUE; } /************************************************************************/ /* GTIFDecToDMS() */ /* */ /* Convenient function to translate decimal degrees to DMS */ /* format for reporting to a user. */ /************************************************************************/ const char *GTIFDecToDMS( double dfAngle, const char * pszAxis, int nPrecision ) { int nDegrees, nMinutes; double dfSeconds; char szFormat[30]; static char szBuffer[50]; const char *pszHemisphere = NULL; double dfRound; int i; dfRound = 0.5/60; for( i = 0; i < nPrecision; i++ ) dfRound = dfRound * 0.1; nDegrees = (int) ABS(dfAngle); nMinutes = (int) ((ABS(dfAngle) - nDegrees) * 60 + dfRound); dfSeconds = ABS((ABS(dfAngle) * 3600 - nDegrees*3600 - nMinutes*60)); if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 ) pszHemisphere = "W"; else if( EQUAL(pszAxis,"Long") ) pszHemisphere = "E"; else if( dfAngle < 0.0 ) pszHemisphere = "S"; else pszHemisphere = "N"; sprintf( szFormat, "%%3dd%%2d\'%%%d.%df\"%s", nPrecision+3, nPrecision, pszHemisphere ); sprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds ); return szBuffer; } /************************************************************************/ /* GTIFPrintDefn() */ /* */ /* Report the contents of a GTIFDefn structure ... mostly for */ /* debugging. */ /************************************************************************/ void GTIFPrintDefn( GTIFDefn * psDefn, FILE * fp ) { /* -------------------------------------------------------------------- */ /* Do we have anything to report? */ /* -------------------------------------------------------------------- */ if( !psDefn->DefnSet ) { fprintf( fp, "No GeoKeys found.\n" ); return; } /* -------------------------------------------------------------------- */ /* Get the PCS name if possible. */ /* -------------------------------------------------------------------- */ if( psDefn->PCS != KvUserDefined ) { char *pszPCSName = NULL; GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL ); if( pszPCSName == NULL ) pszPCSName = CPLStrdup("name unknown"); fprintf( fp, "PCS = %d (%s)\n", psDefn->PCS, pszPCSName ); CPLFree( pszPCSName ); } /* -------------------------------------------------------------------- */ /* Dump the projection code if possible. */ /* -------------------------------------------------------------------- */ if( psDefn->ProjCode != KvUserDefined ) { char *pszTRFName = NULL; GTIFGetProjTRFInfo( psDefn->ProjCode, &pszTRFName, NULL, NULL ); if( pszTRFName == NULL ) pszTRFName = CPLStrdup(""); fprintf( fp, "Projection = %d (%s)\n", psDefn->ProjCode, pszTRFName ); CPLFree( pszTRFName ); } /* -------------------------------------------------------------------- */ /* Try to dump the projection method name, and parameters if possible.*/ /* -------------------------------------------------------------------- */ if( psDefn->CTProjection != KvUserDefined ) { char *pszName = GTIFValueName(ProjCoordTransGeoKey, psDefn->CTProjection); int i; if( pszName == NULL ) pszName = "(unknown)"; fprintf( fp, "Projection Method: %s\n", pszName ); for( i = 0; i < psDefn->nParms; i++ ) { if( psDefn->ProjParmId[i] == 0 ) continue; pszName = GTIFKeyName((geokey_t) psDefn->ProjParmId[i]); if( pszName == NULL ) pszName = "(unknown)"; if( i < 4 ) { char *pszAxisName; if( strstr(pszName,"Long") != NULL ) pszAxisName = "Long"; else if( strstr(pszName,"Lat") != NULL ) pszAxisName = "Lat"; else pszAxisName = "?"; fprintf( fp, " %s: %f (%s)\n", pszName, psDefn->ProjParm[i], GTIFDecToDMS( psDefn->ProjParm[i], pszAxisName, 2 ) ); } else if( i == 4 ) fprintf( fp, " %s: %f\n", pszName, psDefn->ProjParm[i] ); else fprintf( fp, " %s: %f m\n", pszName, psDefn->ProjParm[i] ); } } /* -------------------------------------------------------------------- */ /* Report the GCS name, and number. */ /* -------------------------------------------------------------------- */ if( psDefn->GCS != KvUserDefined ) { char *pszName = NULL; GTIFGetGCSInfo( psDefn->GCS, &pszName, NULL, NULL, NULL ); if( pszName == NULL ) pszName = CPLStrdup("(unknown)"); fprintf( fp, "GCS: %d/%s\n", psDefn->GCS, pszName ); CPLFree( pszName ); } /* -------------------------------------------------------------------- */ /* Report the datum name. */ /* -------------------------------------------------------------------- */ if( psDefn->Datum != KvUserDefined ) { char *pszName = NULL; GTIFGetDatumInfo( psDefn->Datum, &pszName, NULL ); if( pszName == NULL ) pszName = CPLStrdup("(unknown)"); fprintf( fp, "Datum: %d/%s\n", psDefn->Datum, pszName ); CPLFree( pszName ); } /* -------------------------------------------------------------------- */ /* Report the ellipsoid. */ /* -------------------------------------------------------------------- */ if( psDefn->Ellipsoid != KvUserDefined ) { char *pszName = NULL; GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszName, NULL, NULL ); if( pszName == NULL ) pszName = CPLStrdup("(unknown)"); fprintf( fp, "Ellipsoid: %d/%s (%.2f,%.2f)\n", psDefn->Ellipsoid, pszName, psDefn->SemiMajor, psDefn->SemiMinor ); CPLFree( pszName ); } /* -------------------------------------------------------------------- */ /* Report the prime meridian. */ /* -------------------------------------------------------------------- */ if( psDefn->PM != KvUserDefined ) { char *pszName = NULL; GTIFGetPMInfo( psDefn->PM, &pszName, NULL ); if( pszName == NULL ) pszName = CPLStrdup("(unknown)"); fprintf( fp, "Prime Meridian: %d/%s (%f/%s)\n", psDefn->PM, pszName, psDefn->PMLongToGreenwich, GTIFDecToDMS( psDefn->PMLongToGreenwich, "Long", 2 ) ); CPLFree( pszName ); } /* -------------------------------------------------------------------- */ /* Report TOWGS84 parameters. */ /* -------------------------------------------------------------------- */ #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) if( psDefn->TOWGS84Count > 0 ) { int i; fprintf( fp, "TOWGS84: " ); for( i = 0; i < psDefn->TOWGS84Count; i++ ) { if( i > 0 ) fprintf( fp, "," ); fprintf( fp, "%g", psDefn->TOWGS84[i] ); } fprintf( fp, "\n" ); } #endif /* -------------------------------------------------------------------- */ /* Report the projection units of measure (currently just */ /* linear). */ /* -------------------------------------------------------------------- */ if( psDefn->UOMLength != KvUserDefined ) { char *pszName = NULL; GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszName, NULL ); if( pszName == NULL ) pszName = CPLStrdup( "(unknown)" ); fprintf( fp, "Projection Linear Units: %d/%s (%fm)\n", psDefn->UOMLength, pszName, psDefn->UOMLengthInMeters ); CPLFree( pszName ); } else { fprintf( fp, "Projection Linear Units: User-Defined (%fm)\n", psDefn->UOMLengthInMeters ); } } /************************************************************************/ /* GTIFFreeMemory() */ /* */ /* Externally visible function to free memory allocated within */ /* geo_normalize.c. */ /************************************************************************/ void GTIFFreeMemory( char * pMemory ) { if( pMemory != NULL ) VSIFree( pMemory ); } /************************************************************************/ /* GTIFDeaccessCSV() */ /* */ /* Free all cached CSV info. */ /************************************************************************/ void GTIFDeaccessCSV() { CSVDeaccess( NULL ); } /************************************************************************/ /* GTIFAllocDefn() */ /* */ /* This allocates a GTIF structure in such a way that the */ /* calling application doesn't need to know the size and */ /* initializes it appropriately. */ /************************************************************************/ GTIFDefn *GTIFAllocDefn() { return (GTIFDefn *) CPLCalloc(sizeof(GTIFDefn),1); } /************************************************************************/ /* GTIFFreeDefn() */ /* */ /* Free a GTIF structure allocated by GTIFAllocDefn(). */ /************************************************************************/ void GTIFFreeDefn( GTIFDefn *defn ) { VSIFree( defn ); }