mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-19 06:05:31 -06:00
2799 lines
105 KiB
C++
2799 lines
105 KiB
C++
/******************************************************************************
|
|
* $Id: ogr_fromepsg.cpp 28565 2015-02-27 10:26:21Z rouault $
|
|
*
|
|
* Project: OpenGIS Simple Features Reference Implementation
|
|
* Purpose: Generate an OGRSpatialReference object based on an EPSG
|
|
* PROJCS, or GEOGCS code.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2000, Frank Warmerdam
|
|
* Copyright (c) 2008-2013, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* 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 "ogr_spatialref.h"
|
|
#include "ogr_p.h"
|
|
#include "cpl_csv.h"
|
|
#include <vector>
|
|
|
|
CPL_CVSID("$Id: ogr_fromepsg.cpp 28565 2015-02-27 10:26:21Z rouault $");
|
|
|
|
#ifndef PI
|
|
# define PI 3.14159265358979323846
|
|
#endif
|
|
|
|
void OGRPrintDouble( char * pszStrBuf, double dfValue );
|
|
|
|
static const char *papszDatumEquiv[] =
|
|
{
|
|
"Militar_Geographische_Institut",
|
|
"Militar_Geographische_Institute",
|
|
"World_Geodetic_System_1984",
|
|
"WGS_1984",
|
|
"WGS_72_Transit_Broadcast_Ephemeris",
|
|
"WGS_1972_Transit_Broadcast_Ephemeris",
|
|
"World_Geodetic_System_1972",
|
|
"WGS_1972",
|
|
"European_Terrestrial_Reference_System_89",
|
|
"European_Reference_System_1989",
|
|
NULL
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* OGREPSGDatumNameMassage() */
|
|
/* */
|
|
/* Massage an EPSG datum name into WMT format. Also transform */
|
|
/* specific exception cases into WKT versions. */
|
|
/************************************************************************/
|
|
|
|
void OGREPSGDatumNameMassage( char ** ppszDatum )
|
|
|
|
{
|
|
int i, j;
|
|
char *pszDatum = *ppszDatum;
|
|
|
|
if (pszDatum[0] == '\0')
|
|
return;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Translate non-alphanumeric values to underscores. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; pszDatum[i] != '\0'; i++ )
|
|
{
|
|
if( pszDatum[i] != '+'
|
|
&& !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z')
|
|
&& !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z')
|
|
&& !(pszDatum[i] >= '0' && pszDatum[i] <= '9') )
|
|
{
|
|
pszDatum[i] = '_';
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Remove repeated and trailing underscores. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 1, j = 0; pszDatum[i] != '\0'; i++ )
|
|
{
|
|
if( pszDatum[j] == '_' && pszDatum[i] == '_' )
|
|
continue;
|
|
|
|
pszDatum[++j] = pszDatum[i];
|
|
}
|
|
if( pszDatum[j] == '_' )
|
|
pszDatum[j] = '\0';
|
|
else
|
|
pszDatum[j+1] = '\0';
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Search for datum equivelences. Specific massaged names get */
|
|
/* mapped to OpenGIS specified names. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; papszDatumEquiv[i] != NULL; i += 2 )
|
|
{
|
|
if( EQUAL(*ppszDatum,papszDatumEquiv[i]) )
|
|
{
|
|
CPLFree( *ppszDatum );
|
|
*ppszDatum = CPLStrdup( papszDatumEquiv[i+1] );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGAngleStringToDD() */
|
|
/* */
|
|
/* Convert an angle in the specified units to decimal degrees. */
|
|
/************************************************************************/
|
|
|
|
static double
|
|
EPSGAngleStringToDD( const char * pszAngle, int nUOMAngle )
|
|
|
|
{
|
|
double dfAngle;
|
|
|
|
if( nUOMAngle == 9110 ) /* DDD.MMSSsss */
|
|
{
|
|
char *pszDecimal;
|
|
|
|
dfAngle = ABS(atoi(pszAngle));
|
|
pszDecimal = (char *) 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 += CPLAtof(szSeconds) / 3600.0;
|
|
}
|
|
}
|
|
|
|
if( pszAngle[0] == '-' )
|
|
dfAngle *= -1;
|
|
}
|
|
else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */
|
|
{
|
|
dfAngle = 180 * (CPLAtof(pszAngle ) / 200);
|
|
}
|
|
else if( nUOMAngle == 9101 ) /* radians */
|
|
{
|
|
dfAngle = 180 * (CPLAtof(pszAngle ) / PI);
|
|
}
|
|
else if( nUOMAngle == 9103 ) /* arc-minute */
|
|
{
|
|
dfAngle = CPLAtof(pszAngle) / 60;
|
|
}
|
|
else if( nUOMAngle == 9104 ) /* arc-second */
|
|
{
|
|
dfAngle = CPLAtof(pszAngle) / 3600;
|
|
}
|
|
else /* decimal degrees ... some cases missing but seeminly never used */
|
|
{
|
|
CPLAssert( nUOMAngle == 9102 || nUOMAngle == 0 );
|
|
|
|
dfAngle = CPLAtof(pszAngle );
|
|
}
|
|
|
|
return( dfAngle );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetUOMAngleInfo() */
|
|
/************************************************************************/
|
|
|
|
int EPSGGetUOMAngleInfo( int nUOMAngleCode,
|
|
char **ppszUOMName,
|
|
double * pdfInDegrees )
|
|
|
|
{
|
|
const char *pszUOMName = NULL;
|
|
double dfInDegrees = 1.0;
|
|
const char *pszFilename;
|
|
char szSearchKey[24];
|
|
|
|
/* We do a special override of some of the DMS formats name */
|
|
/* This will also solve accuracy problems when computing */
|
|
/* the dfInDegree value from the CSV values (#3643) */
|
|
if( nUOMAngleCode == 9102 || nUOMAngleCode == 9107
|
|
|| nUOMAngleCode == 9108 || nUOMAngleCode == 9110
|
|
|| nUOMAngleCode == 9122 )
|
|
{
|
|
if( ppszUOMName != NULL )
|
|
*ppszUOMName = CPLStrdup("degree");
|
|
if( pdfInDegrees != NULL )
|
|
*pdfInDegrees = 1.0;
|
|
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;
|
|
|
|
dfFactorB =
|
|
CPLAtof(CSVGetField( pszFilename,
|
|
"UOM_CODE", szSearchKey, CC_Integer,
|
|
"FACTOR_B" ));
|
|
|
|
dfFactorC =
|
|
CPLAtof(CSVGetField( pszFilename,
|
|
"UOM_CODE", szSearchKey, CC_Integer,
|
|
"FACTOR_C" ));
|
|
|
|
if( dfFactorC != 0.0 )
|
|
dfInDegrees = (dfFactorB / dfFactorC) * (180.0 / PI);
|
|
|
|
// For some reason, (FactorB) is not very precise in EPSG, use
|
|
// a more exact form for grads.
|
|
if( nUOMAngleCode == 9105 )
|
|
dfInDegrees = 180.0 / 200.0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Otherwise handle a few well known units directly. */
|
|
/* -------------------------------------------------------------------- */
|
|
else
|
|
{
|
|
switch( nUOMAngleCode )
|
|
{
|
|
case 9101:
|
|
pszUOMName = "radian";
|
|
dfInDegrees = 180.0 / 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 / (3.14159265358979 * 1000000.0);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Return to caller. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszUOMName != NULL )
|
|
{
|
|
if( pszUOMName != NULL )
|
|
*ppszUOMName = CPLStrdup( pszUOMName );
|
|
else
|
|
*ppszUOMName = NULL;
|
|
}
|
|
|
|
if( pdfInDegrees != NULL )
|
|
*pdfInDegrees = dfInDegrees;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetUOMLengthInfo() */
|
|
/* */
|
|
/* Note: This function should eventually also know how to */
|
|
/* lookup length aliases in the UOM_LE_ALIAS table. */
|
|
/************************************************************************/
|
|
|
|
static int
|
|
EPSGGetUOMLengthInfo( int nUOMLengthCode,
|
|
char **ppszUOMName,
|
|
double * pdfInMeters )
|
|
|
|
{
|
|
char **papszUnitsRecord;
|
|
char szSearchKey[24];
|
|
int iNameField;
|
|
|
|
#define UOM_FILENAME CSVFilename( "unit_of_measure.csv" )
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We short cut meter to save work in the most common case. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nUOMLengthCode == 9001 )
|
|
{
|
|
if( ppszUOMName != NULL )
|
|
*ppszUOMName = CPLStrdup( "metre" );
|
|
if( pdfInMeters != NULL )
|
|
*pdfInMeters = 1.0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Search the units database for this unit. If we don't find */
|
|
/* it return failure. */
|
|
/* -------------------------------------------------------------------- */
|
|
sprintf( szSearchKey, "%d", nUOMLengthCode );
|
|
papszUnitsRecord =
|
|
CSVScanFileByName( UOM_FILENAME, "UOM_CODE", szSearchKey, CC_Integer );
|
|
|
|
if( papszUnitsRecord == NULL )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the name, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszUOMName != NULL )
|
|
{
|
|
iNameField = CSVGetFileFieldId( UOM_FILENAME, "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( UOM_FILENAME, "FACTOR_B" );
|
|
iCFactorField = CSVGetFileFieldId( UOM_FILENAME, "FACTOR_C" );
|
|
|
|
if( CPLAtof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 )
|
|
*pdfInMeters = CPLAtof(CSLGetField(papszUnitsRecord,iBFactorField))
|
|
/ CPLAtof(CSLGetField(papszUnitsRecord, iCFactorField));
|
|
else
|
|
*pdfInMeters = 0.0;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGNegateString() */
|
|
/************************************************************************/
|
|
|
|
static void EPSGNegateString(CPLString& osValue)
|
|
{
|
|
if( osValue.compare("0") == 0 )
|
|
return;
|
|
if( osValue[0] == '-' )
|
|
{
|
|
osValue = osValue.substr(1);
|
|
return;
|
|
}
|
|
if( osValue[0] == '+' )
|
|
{
|
|
osValue[0] = '-';
|
|
return;
|
|
}
|
|
osValue = "-" + osValue;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetWGS84Transform() */
|
|
/* */
|
|
/* The following code attempts to find a bursa-wolf */
|
|
/* transformation from this GeogCS to WGS84 (4326). */
|
|
/* */
|
|
/* Faults: */
|
|
/* o I think there are codes other than 9603 and 9607 that */
|
|
/* return compatible, or easily transformed parameters. */
|
|
/* o Only the first path from the given GeogCS is checked due */
|
|
/* to limitations in the CSV API. */
|
|
/************************************************************************/
|
|
|
|
int EPSGGetWGS84Transform( int nGeogCS, std::vector<CPLString>& asTransform )
|
|
|
|
{
|
|
int nMethodCode, iDXField, iField;
|
|
char szCode[32];
|
|
const char *pszFilename;
|
|
char **papszLine;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch the line from the GCS table. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszFilename = CSVFilename("gcs.override.csv");
|
|
sprintf( szCode, "%d", nGeogCS );
|
|
papszLine = CSVScanFileByName( pszFilename,
|
|
"COORD_REF_SYS_CODE",
|
|
szCode, CC_Integer );
|
|
if( papszLine == NULL )
|
|
{
|
|
pszFilename = CSVFilename("gcs.csv");
|
|
sprintf( szCode, "%d", nGeogCS );
|
|
papszLine = CSVScanFileByName( pszFilename,
|
|
"COORD_REF_SYS_CODE",
|
|
szCode, CC_Integer );
|
|
}
|
|
|
|
if( papszLine == NULL )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that the method code is one of our accepted ones. */
|
|
/* -------------------------------------------------------------------- */
|
|
nMethodCode =
|
|
atoi(CSLGetField( papszLine,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"COORD_OP_METHOD_CODE")));
|
|
if( nMethodCode != 9603 && nMethodCode != 9607 && nMethodCode != 9606 )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch the transformation parameters. */
|
|
/* -------------------------------------------------------------------- */
|
|
iDXField = CSVGetFileFieldId(pszFilename, "DX");
|
|
if (iDXField < 0 || CSLCount(papszLine) < iDXField + 7)
|
|
return FALSE;
|
|
|
|
asTransform.resize(0);
|
|
for( iField = 0; iField < 7; iField++ )
|
|
{
|
|
const char* pszValue = papszLine[iDXField+iField];
|
|
if( pszValue[0] )
|
|
asTransform.push_back(pszValue);
|
|
else
|
|
asTransform.push_back("0");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 9607 - coordinate frame rotation has reverse signs on the */
|
|
/* rotational coefficients. Fix up now since we internal */
|
|
/* operate according to method 9606 (position vector 7-parameter). */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nMethodCode == 9607 )
|
|
{
|
|
EPSGNegateString(asTransform[3]);
|
|
EPSGNegateString(asTransform[4]);
|
|
EPSGNegateString(asTransform[5]);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetPMInfo() */
|
|
/* */
|
|
/* Get the offset between a given prime meridian and Greenwich */
|
|
/* in degrees. */
|
|
/************************************************************************/
|
|
|
|
static int
|
|
EPSGGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset )
|
|
|
|
{
|
|
char szSearchKey[24];
|
|
int nUOMAngle;
|
|
|
|
#define PM_FILENAME CSVFilename("prime_meridian.csv")
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Use a special short cut for Greenwich, since it is so common. */
|
|
/* -------------------------------------------------------------------- */
|
|
/* FIXME? Where does 7022 come from ? Let's keep it just in case */
|
|
/* 8901 is the official current code for Greenwich */
|
|
if( nPMCode == 7022 /* PM_Greenwich */ || nPMCode == 8901 )
|
|
{
|
|
if( pdfOffset != NULL )
|
|
*pdfOffset = 0.0;
|
|
if( ppszName != NULL )
|
|
*ppszName = CPLStrdup( "Greenwich" );
|
|
return TRUE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Search the database for the corresponding datum code. */
|
|
/* -------------------------------------------------------------------- */
|
|
sprintf( szSearchKey, "%d", nPMCode );
|
|
|
|
nUOMAngle =
|
|
atoi(CSVGetField( PM_FILENAME,
|
|
"PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
|
|
"UOM_CODE" ) );
|
|
if( nUOMAngle < 1 )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the PM offset. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pdfOffset != NULL )
|
|
{
|
|
*pdfOffset =
|
|
EPSGAngleStringToDD(
|
|
CSVGetField( PM_FILENAME,
|
|
"PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
|
|
"GREENWICH_LONGITUDE" ),
|
|
nUOMAngle );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the name, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszName != NULL )
|
|
*ppszName =
|
|
CPLStrdup(
|
|
CSVGetField( PM_FILENAME,
|
|
"PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
|
|
"PRIME_MERIDIAN_NAME" ));
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetGCSInfo() */
|
|
/* */
|
|
/* Fetch the datum, and prime meridian related to a particular */
|
|
/* GCS. */
|
|
/************************************************************************/
|
|
|
|
static int
|
|
EPSGGetGCSInfo( int nGCSCode, char ** ppszName,
|
|
int * pnDatum, char **ppszDatumName,
|
|
int * pnPM, int *pnEllipsoid, int *pnUOMAngle,
|
|
int * pnCoordSysCode )
|
|
|
|
{
|
|
char szSearchKey[24];
|
|
int nDatum, nPM, nUOMAngle, nEllipsoid;
|
|
const char *pszFilename;
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 )
|
|
return FALSE;
|
|
|
|
if( pnDatum != NULL )
|
|
*pnDatum = nDatum;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the PM. */
|
|
/* -------------------------------------------------------------------- */
|
|
nPM = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer,
|
|
"PRIME_MERIDIAN_CODE" ) );
|
|
|
|
if( nPM < 1 )
|
|
return FALSE;
|
|
|
|
if( pnPM != NULL )
|
|
*pnPM = nPM;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the Ellipsoid. */
|
|
/* -------------------------------------------------------------------- */
|
|
nEllipsoid = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer,
|
|
"ELLIPSOID_CODE" ) );
|
|
|
|
if( nEllipsoid < 1 )
|
|
return FALSE;
|
|
|
|
if( pnEllipsoid != NULL )
|
|
*pnEllipsoid = nEllipsoid;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 = nUOMAngle;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the name, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszName != NULL )
|
|
*ppszName =
|
|
CPLStrdup(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer,
|
|
"COORD_REF_SYS_NAME" ));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the datum name, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszDatumName != NULL )
|
|
*ppszDatumName =
|
|
CPLStrdup(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer,
|
|
"DATUM_NAME" ));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the CoordSysCode */
|
|
/* -------------------------------------------------------------------- */
|
|
int nCSC;
|
|
|
|
nCSC = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer,
|
|
"COORD_SYS_CODE" ) );
|
|
|
|
if( pnCoordSysCode != NULL )
|
|
*pnCoordSysCode = nCSC;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSRGetEllipsoidInfo() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Fetch info about an ellipsoid.
|
|
*
|
|
* This helper function will return ellipsoid parameters corresponding to EPSG
|
|
* code provided. Axes are always returned in meters. Semi major computed
|
|
* based on inverse flattening where that is provided.
|
|
*
|
|
* @param nCode EPSG code of the requested ellipsoid
|
|
*
|
|
* @param ppszName pointer to string where ellipsoid name will be returned. It
|
|
* is caller responsibility to free this string after using with CPLFree().
|
|
*
|
|
* @param pdfSemiMajor pointer to variable where semi major axis will be
|
|
* returned.
|
|
*
|
|
* @param pdfInvFlattening pointer to variable where inverse flattening will
|
|
* be returned.
|
|
*
|
|
* @return OGRERR_NONE on success or an error code in case of failure.
|
|
**/
|
|
|
|
OGRErr
|
|
OSRGetEllipsoidInfo( int nCode, char ** ppszName,
|
|
double * pdfSemiMajor, double * pdfInvFlattening )
|
|
|
|
{
|
|
char szSearchKey[24];
|
|
double dfSemiMajor, dfToMeters = 1.0;
|
|
int nUOMLength;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the semi major axis. */
|
|
/* -------------------------------------------------------------------- */
|
|
snprintf( szSearchKey, sizeof(szSearchKey), "%d", nCode );
|
|
szSearchKey[sizeof(szSearchKey) - 1] = '\n';
|
|
|
|
dfSemiMajor =
|
|
CPLAtof(CSVGetField( CSVFilename("ellipsoid.csv" ),
|
|
"ELLIPSOID_CODE", szSearchKey, CC_Integer,
|
|
"SEMI_MAJOR_AXIS" ) );
|
|
if( dfSemiMajor == 0.0 )
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the translation factor into meters. */
|
|
/* -------------------------------------------------------------------- */
|
|
nUOMLength = atoi(CSVGetField( CSVFilename("ellipsoid.csv" ),
|
|
"ELLIPSOID_CODE", szSearchKey, CC_Integer,
|
|
"UOM_CODE" ));
|
|
EPSGGetUOMLengthInfo( 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( pdfInvFlattening != NULL )
|
|
{
|
|
*pdfInvFlattening =
|
|
CPLAtof(CSVGetField( CSVFilename("ellipsoid.csv" ),
|
|
"ELLIPSOID_CODE", szSearchKey, CC_Integer,
|
|
"INV_FLATTENING" ));
|
|
|
|
if( *pdfInvFlattening == 0.0 )
|
|
{
|
|
double dfSemiMinor;
|
|
|
|
dfSemiMinor =
|
|
CPLAtof(CSVGetField( CSVFilename("ellipsoid.csv" ),
|
|
"ELLIPSOID_CODE", szSearchKey, CC_Integer,
|
|
"SEMI_MINOR_AXIS" )) * dfToMeters;
|
|
|
|
if( dfSemiMajor == 0.0 )
|
|
*pdfInvFlattening = 0.0;
|
|
else
|
|
*pdfInvFlattening = OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the name, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszName != NULL )
|
|
*ppszName =
|
|
CPLStrdup(CSVGetField( CSVFilename("ellipsoid.csv" ),
|
|
"ELLIPSOID_CODE", szSearchKey, CC_Integer,
|
|
"ELLIPSOID_NAME" ));
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
#define CoLatConeAxis 1036 /* see #4223 */
|
|
#define NatOriginLat 8801
|
|
#define NatOriginLong 8802
|
|
#define NatOriginScaleFactor 8805
|
|
#define FalseEasting 8806
|
|
#define FalseNorthing 8807
|
|
#define ProjCenterLat 8811
|
|
#define ProjCenterLong 8812
|
|
#define Azimuth 8813
|
|
#define AngleRectifiedToSkewedGrid 8814
|
|
#define InitialLineScaleFactor 8815
|
|
#define ProjCenterEasting 8816
|
|
#define ProjCenterNorthing 8817
|
|
#define PseudoStdParallelLat 8818
|
|
#define PseudoStdParallelScaleFactor 8819
|
|
#define FalseOriginLat 8821
|
|
#define FalseOriginLong 8822
|
|
#define StdParallel1Lat 8823
|
|
#define StdParallel2Lat 8824
|
|
#define FalseOriginEasting 8826
|
|
#define FalseOriginNorthing 8827
|
|
#define SphericalOriginLat 8828
|
|
#define SphericalOriginLong 8829
|
|
#define InitialLongitude 8830
|
|
#define ZoneWidth 8831
|
|
#define PolarLatStdParallel 8832
|
|
#define PolarLongOrigin 8833
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetProjTRFInfo() */
|
|
/* */
|
|
/* 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. */
|
|
/************************************************************************/
|
|
|
|
static int
|
|
EPSGGetProjTRFInfo( int nPCS, int * pnProjMethod,
|
|
int *panParmIds, double * padfProjParms )
|
|
|
|
{
|
|
int nProjMethod, i;
|
|
double adfProjParms[7];
|
|
char szTRFCode[16];
|
|
CPLString osFilename;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the proj method. If this fails to return a meaningful */
|
|
/* number, then the whole function fails. */
|
|
/* -------------------------------------------------------------------- */
|
|
osFilename = CSVFilename( "pcs.override.csv" );
|
|
sprintf( szTRFCode, "%d", nPCS );
|
|
nProjMethod =
|
|
atoi( CSVGetField( osFilename,
|
|
"COORD_REF_SYS_CODE", szTRFCode, CC_Integer,
|
|
"COORD_OP_METHOD_CODE" ) );
|
|
if( nProjMethod == 0 )
|
|
{
|
|
osFilename = CSVFilename( "pcs.csv" );
|
|
sprintf( szTRFCode, "%d", nPCS );
|
|
nProjMethod =
|
|
atoi( CSVGetField( osFilename,
|
|
"COORD_REF_SYS_CODE", szTRFCode, CC_Integer,
|
|
"COORD_OP_METHOD_CODE" ) );
|
|
}
|
|
|
|
if( nProjMethod == 0 )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the parameters for this projection. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
for( i = 0; i < 7; i++ )
|
|
{
|
|
char szParamUOMID[32], szParamValueID[32], szParamCodeID[32];
|
|
char *pszValue;
|
|
int nUOM;
|
|
|
|
sprintf( szParamCodeID, "PARAMETER_CODE_%d", i+1 );
|
|
sprintf( szParamUOMID, "PARAMETER_UOM_%d", i+1 );
|
|
sprintf( szParamValueID, "PARAMETER_VALUE_%d", i+1 );
|
|
|
|
if( panParmIds != NULL )
|
|
panParmIds[i] =
|
|
atoi(CSVGetField( osFilename, "COORD_REF_SYS_CODE", szTRFCode,
|
|
CC_Integer, szParamCodeID ));
|
|
|
|
nUOM = atoi(CSVGetField( osFilename, "COORD_REF_SYS_CODE", szTRFCode,
|
|
CC_Integer, szParamUOMID ));
|
|
pszValue = CPLStrdup(
|
|
CSVGetField( osFilename, "COORD_REF_SYS_CODE", szTRFCode,
|
|
CC_Integer, szParamValueID ));
|
|
|
|
// there is a bug in the EPSG 6.2.2 database for PCS 2935 and 2936
|
|
// such that they have foot units for the scale factor. Avoid this.
|
|
if( (panParmIds[i] == NatOriginScaleFactor
|
|
|| panParmIds[i] == InitialLineScaleFactor
|
|
|| panParmIds[i] == PseudoStdParallelScaleFactor)
|
|
&& nUOM < 9200 )
|
|
nUOM = 9201;
|
|
|
|
if( nUOM >= 9100 && nUOM < 9200 )
|
|
adfProjParms[i] = EPSGAngleStringToDD( pszValue, nUOM );
|
|
else if( nUOM > 9000 && nUOM < 9100 )
|
|
{
|
|
double dfInMeters;
|
|
|
|
if( !EPSGGetUOMLengthInfo( nUOM, NULL, &dfInMeters ) )
|
|
dfInMeters = 1.0;
|
|
adfProjParms[i] = CPLAtof(pszValue) * dfInMeters;
|
|
}
|
|
else if( EQUAL(pszValue,"") ) /* null field */
|
|
{
|
|
adfProjParms[i] = 0.0;
|
|
}
|
|
else /* really we should consider looking up other scaling factors */
|
|
{
|
|
if( nUOM != 9201 )
|
|
CPLDebug( "OGR",
|
|
"Non-unity scale factor units! (UOM=%d, PCS=%d)",
|
|
nUOM, nPCS );
|
|
adfProjParms[i] = CPLAtof(pszValue);
|
|
}
|
|
|
|
CPLFree( pszValue );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Transfer requested data into passed variables. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pnProjMethod != NULL )
|
|
*pnProjMethod = nProjMethod;
|
|
|
|
if( padfProjParms != NULL )
|
|
{
|
|
for( i = 0; i < 7; i++ )
|
|
padfProjParms[i] = adfProjParms[i];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* EPSGGetPCSInfo() */
|
|
/************************************************************************/
|
|
|
|
static int
|
|
EPSGGetPCSInfo( int nPCSCode, char **ppszEPSGName,
|
|
int *pnUOMLengthCode, int *pnUOMAngleCode,
|
|
int *pnGeogCS, int *pnTRFCode, int *pnCoordSysCode )
|
|
|
|
{
|
|
char **papszRecord;
|
|
char szSearchKey[24];
|
|
const char *pszFilename;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Search the units database for this unit. If we don't find */
|
|
/* it return failure. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszFilename = CSVFilename( "pcs.csv" );
|
|
sprintf( szSearchKey, "%d", nPCSCode );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
if( papszRecord == NULL )
|
|
{
|
|
pszFilename = CSVFilename( "pcs.override.csv" );
|
|
sprintf( szSearchKey, "%d", nPCSCode );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
}
|
|
|
|
if( papszRecord == NULL )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the name, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( ppszEPSGName != NULL )
|
|
{
|
|
CPLString osPCSName =
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"COORD_REF_SYS_NAME"));
|
|
|
|
const char *pszDeprecated =
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"DEPRECATED") );
|
|
|
|
if( pszDeprecated != NULL && *pszDeprecated == '1' )
|
|
osPCSName += " (deprecated)";
|
|
|
|
*ppszEPSGName = CPLStrdup(osPCSName);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 = atoi(pszValue);
|
|
else
|
|
*pnUOMLengthCode = 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the UOM Angle code, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pnUOMAngleCode != NULL )
|
|
{
|
|
const char *pszValue;
|
|
|
|
pszValue =
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,"UOM_ANGLE_CODE") );
|
|
|
|
if( atoi(pszValue) > 0 )
|
|
*pnUOMAngleCode = atoi(pszValue);
|
|
else
|
|
*pnUOMAngleCode = 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 = atoi(pszValue);
|
|
else
|
|
*pnGeogCS = 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the GeogCS (Datum with PM) code, if requested. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pnTRFCode != NULL )
|
|
{
|
|
const char *pszValue;
|
|
|
|
pszValue =
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,"COORD_OP_CODE"));
|
|
|
|
|
|
if( atoi(pszValue) > 0 )
|
|
*pnTRFCode = atoi(pszValue);
|
|
else
|
|
*pnTRFCode = 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the CoordSysCode */
|
|
/* -------------------------------------------------------------------- */
|
|
int nCSC;
|
|
|
|
nCSC = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer,
|
|
"COORD_SYS_CODE" ) );
|
|
|
|
if( pnCoordSysCode != NULL )
|
|
*pnCoordSysCode = nCSC;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetEPSGAxisInfo() */
|
|
/************************************************************************/
|
|
|
|
static OGRErr SetEPSGAxisInfo( OGRSpatialReference *poSRS,
|
|
const char *pszTargetKey,
|
|
int nCoordSysCode )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special cases for well known and common values. We short */
|
|
/* circuit these to save time doing file lookups. */
|
|
/* -------------------------------------------------------------------- */
|
|
// Conventional and common Easting/Northing values.
|
|
if( nCoordSysCode >= 4400 && nCoordSysCode <= 4410 )
|
|
{
|
|
return
|
|
poSRS->SetAxes( pszTargetKey,
|
|
"Easting", OAO_East,
|
|
"Northing", OAO_North );
|
|
}
|
|
|
|
// Conventional and common Easting/Northing values.
|
|
if( nCoordSysCode >= 6400 && nCoordSysCode <= 6423 )
|
|
{
|
|
return
|
|
poSRS->SetAxes( pszTargetKey,
|
|
"Latitude", OAO_North,
|
|
"Longitude", OAO_East );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the definition from the coordinate_axis.csv file. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszRecord;
|
|
char **papszAxis1=NULL, **papszAxis2=NULL;
|
|
char szSearchKey[24];
|
|
const char *pszFilename;
|
|
|
|
pszFilename = CSVFilename( "coordinate_axis.csv" );
|
|
sprintf( szSearchKey, "%d", nCoordSysCode );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
if( papszRecord != NULL )
|
|
{
|
|
papszAxis1 = CSLDuplicate( papszRecord );
|
|
papszRecord = CSVGetNextLine( pszFilename );
|
|
if( CSLCount(papszRecord) > 0
|
|
&& EQUAL(papszRecord[0],papszAxis1[0]) )
|
|
papszAxis2 = CSLDuplicate( papszRecord );
|
|
}
|
|
|
|
if( papszAxis2 == NULL )
|
|
{
|
|
CSLDestroy( papszAxis1 );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Failed to find entries for COORD_SYS_CODE %d in coordinate_axis.csv",
|
|
nCoordSysCode );
|
|
return OGRERR_FAILURE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Confirm the records are complete, and work out which columns */
|
|
/* are which. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iAxisOrientationField, iAxisAbbrevField, iAxisOrderField;
|
|
int iAxisNameCodeField;
|
|
|
|
iAxisOrientationField =
|
|
CSVGetFileFieldId( pszFilename, "coord_axis_orientation" );
|
|
iAxisAbbrevField =
|
|
CSVGetFileFieldId( pszFilename, "coord_axis_abbreviation" );
|
|
iAxisOrderField =
|
|
CSVGetFileFieldId( pszFilename, "coord_axis_order" );
|
|
iAxisNameCodeField =
|
|
CSVGetFileFieldId( pszFilename, "coord_axis_name_code" );
|
|
|
|
/* Check that all fields are available and that the axis_order field */
|
|
/* is the one with highest index */
|
|
if ( !( iAxisOrientationField >= 0 &&
|
|
iAxisOrientationField < iAxisOrderField &&
|
|
iAxisAbbrevField >= 0 &&
|
|
iAxisAbbrevField < iAxisOrderField &&
|
|
iAxisOrderField >= 0 &&
|
|
iAxisNameCodeField >= 0 &&
|
|
iAxisNameCodeField < iAxisOrderField ) )
|
|
{
|
|
CSLDestroy( papszAxis1 );
|
|
CSLDestroy( papszAxis2 );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"coordinate_axis.csv corrupted" );
|
|
return OGRERR_FAILURE;
|
|
}
|
|
|
|
if( CSLCount(papszAxis1) < iAxisOrderField+1
|
|
|| CSLCount(papszAxis2) < iAxisOrderField+1 )
|
|
{
|
|
CSLDestroy( papszAxis1 );
|
|
CSLDestroy( papszAxis2 );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Axis records appear incomplete for COORD_SYS_CODE %d in coordinate_axis.csv",
|
|
nCoordSysCode );
|
|
return OGRERR_FAILURE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we need to switch the axes around? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( atoi(papszAxis2[iAxisOrderField]) < atoi(papszAxis1[iAxisOrderField]) )
|
|
{
|
|
papszRecord = papszAxis1;
|
|
papszAxis1 = papszAxis2;
|
|
papszAxis2 = papszRecord;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Work out axis enumeration values. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRAxisOrientation eOAxis1 = OAO_Other, eOAxis2 = OAO_Other;
|
|
int iAO;
|
|
static int anCodes[7] = { -1, 9907, 9909, 9906, 9908, -1, -1 };
|
|
|
|
for( iAO = 0; iAO < 7; iAO++ )
|
|
{
|
|
if( EQUAL(papszAxis1[iAxisOrientationField],
|
|
OSRAxisEnumToName((OGRAxisOrientation) iAO)) )
|
|
eOAxis1 = (OGRAxisOrientation) iAO;
|
|
if( EQUAL(papszAxis2[iAxisOrientationField],
|
|
OSRAxisEnumToName((OGRAxisOrientation) iAO)) )
|
|
eOAxis2 = (OGRAxisOrientation) iAO;
|
|
|
|
if( eOAxis1 == OAO_Other
|
|
&& anCodes[iAO] == atoi(papszAxis1[iAxisNameCodeField]) )
|
|
eOAxis1 = (OGRAxisOrientation) iAO;
|
|
if( eOAxis2 == OAO_Other
|
|
&& anCodes[iAO] == atoi(papszAxis2[iAxisNameCodeField]) )
|
|
eOAxis2 = (OGRAxisOrientation) iAO;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Work out the axis name. We try to expand the abbreviation */
|
|
/* to a longer name. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *apszAxisName[2];
|
|
apszAxisName[0] = papszAxis1[iAxisAbbrevField];
|
|
apszAxisName[1] = papszAxis2[iAxisAbbrevField];
|
|
|
|
for( iAO = 0; iAO < 2; iAO++ )
|
|
{
|
|
if( EQUAL(apszAxisName[iAO],"N") )
|
|
apszAxisName[iAO] = "Northing";
|
|
else if( EQUAL(apszAxisName[iAO],"E") )
|
|
apszAxisName[iAO] = "Easting";
|
|
else if( EQUAL(apszAxisName[iAO],"S") )
|
|
apszAxisName[iAO] = "Southing";
|
|
else if( EQUAL(apszAxisName[iAO],"W") )
|
|
apszAxisName[iAO] = "Westing";
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the axes. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRErr eResult;
|
|
eResult = poSRS->SetAxes( pszTargetKey,
|
|
apszAxisName[0], eOAxis1,
|
|
apszAxisName[1], eOAxis2 );
|
|
|
|
CSLDestroy( papszAxis1 );
|
|
CSLDestroy( papszAxis2 );
|
|
|
|
return eResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetEPSGGeogCS() */
|
|
/* */
|
|
/* FLAWS: */
|
|
/* o Units are all hardcoded. */
|
|
/************************************************************************/
|
|
|
|
static OGRErr SetEPSGGeogCS( OGRSpatialReference * poSRS, int nGeogCS )
|
|
|
|
{
|
|
int nDatumCode, nPMCode, nUOMAngle, nEllipsoidCode, nCSC;
|
|
char *pszGeogCSName = NULL, *pszDatumName = NULL, *pszEllipsoidName = NULL;
|
|
char *pszPMName = NULL, *pszAngleName = NULL;
|
|
double dfPMOffset, dfSemiMajor, dfInvFlattening;
|
|
double dfAngleInDegrees, dfAngleInRadians;
|
|
|
|
if( !EPSGGetGCSInfo( nGeogCS, &pszGeogCSName,
|
|
&nDatumCode, &pszDatumName,
|
|
&nPMCode, &nEllipsoidCode, &nUOMAngle, &nCSC ) )
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
|
|
if( !EPSGGetPMInfo( nPMCode, &pszPMName, &dfPMOffset ) )
|
|
{
|
|
CPLFree( pszDatumName );
|
|
CPLFree( pszGeogCSName );
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
OGREPSGDatumNameMassage( &pszDatumName );
|
|
|
|
if( OSRGetEllipsoidInfo( nEllipsoidCode, &pszEllipsoidName,
|
|
&dfSemiMajor, &dfInvFlattening ) != OGRERR_NONE )
|
|
{
|
|
CPLFree( pszDatumName );
|
|
CPLFree( pszGeogCSName );
|
|
CPLFree( pszPMName );
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
if( !EPSGGetUOMAngleInfo( nUOMAngle, &pszAngleName, &dfAngleInDegrees ) )
|
|
{
|
|
pszAngleName = CPLStrdup("degree");
|
|
dfAngleInDegrees = 1.0;
|
|
nUOMAngle = -1;
|
|
}
|
|
|
|
if( dfAngleInDegrees == 1.0 )
|
|
dfAngleInRadians = CPLAtof(SRS_UA_DEGREE_CONV);
|
|
else
|
|
dfAngleInRadians = CPLAtof(SRS_UA_DEGREE_CONV) * dfAngleInDegrees;
|
|
|
|
poSRS->SetGeogCS( pszGeogCSName, pszDatumName,
|
|
pszEllipsoidName, dfSemiMajor, dfInvFlattening,
|
|
pszPMName, dfPMOffset,
|
|
pszAngleName, dfAngleInRadians );
|
|
|
|
std::vector<CPLString> asBursaTransform;
|
|
if( EPSGGetWGS84Transform( nGeogCS, asBursaTransform ) )
|
|
{
|
|
OGR_SRSNode *poWGS84;
|
|
|
|
poWGS84 = new OGR_SRSNode( "TOWGS84" );
|
|
|
|
for( int iCoeff = 0; iCoeff < 7; iCoeff++ )
|
|
{
|
|
poWGS84->AddChild( new OGR_SRSNode( asBursaTransform[iCoeff].c_str() ) );
|
|
}
|
|
|
|
poSRS->GetAttrNode( "DATUM" )->AddChild( poWGS84 );
|
|
}
|
|
|
|
poSRS->SetAuthority( "GEOGCS", "EPSG", nGeogCS );
|
|
poSRS->SetAuthority( "DATUM", "EPSG", nDatumCode );
|
|
poSRS->SetAuthority( "SPHEROID", "EPSG", nEllipsoidCode );
|
|
poSRS->SetAuthority( "PRIMEM", "EPSG", nPMCode );
|
|
|
|
if( nUOMAngle > 0 )
|
|
poSRS->SetAuthority( "GEOGCS|UNIT", "EPSG", nUOMAngle );
|
|
|
|
CPLFree( pszAngleName );
|
|
CPLFree( pszDatumName );
|
|
CPLFree( pszEllipsoidName );
|
|
CPLFree( pszGeogCSName );
|
|
CPLFree( pszPMName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set axes */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nCSC > 0 )
|
|
{
|
|
SetEPSGAxisInfo( poSRS, "GEOGCS", nCSC );
|
|
CPLErrorReset();
|
|
}
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OGR_FetchParm() */
|
|
/* */
|
|
/* Fetch a parameter from the parm list, based on it's EPSG */
|
|
/* parameter code. */
|
|
/************************************************************************/
|
|
|
|
static double OGR_FetchParm( double *padfProjParms,
|
|
int *panParmIds,
|
|
int nTargetId,
|
|
CPL_UNUSED double dfFromGreenwich )
|
|
{
|
|
int i;
|
|
double dfResult;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set default in meters/degrees. */
|
|
/* -------------------------------------------------------------------- */
|
|
switch( nTargetId )
|
|
{
|
|
case NatOriginScaleFactor:
|
|
case InitialLineScaleFactor:
|
|
case PseudoStdParallelScaleFactor:
|
|
dfResult = 1.0;
|
|
break;
|
|
|
|
case AngleRectifiedToSkewedGrid:
|
|
dfResult = 90.0;
|
|
break;
|
|
|
|
default:
|
|
dfResult = 0.0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to find actual value in parameter list. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; i < 7; i++ )
|
|
{
|
|
if( panParmIds[i] == nTargetId )
|
|
{
|
|
dfResult = padfProjParms[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* EPSG longitudes are relative to greenwich. The follow code */
|
|
/* could be used to make them relative to the prime meridian of */
|
|
/* the associated GCS if that was appropriate. However, the */
|
|
/* SetNormProjParm() method expects longitudes relative to */
|
|
/* greenwich, so there is nothing for us to do. */
|
|
/* -------------------------------------------------------------------- */
|
|
#ifdef notdef
|
|
switch( nTargetId )
|
|
{
|
|
case NatOriginLong:
|
|
case ProjCenterLong:
|
|
case FalseOriginLong:
|
|
case SphericalOriginLong:
|
|
case InitialLongitude:
|
|
// Note that the EPSG values are already relative to greenwich.
|
|
// This shift is really making it relative to the provided prime
|
|
// meridian, so that when SetTM() and company the correction back
|
|
// ends up back relative to greenwich.
|
|
dfResult = dfResult + dfFromGreenwich;
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
#endif
|
|
|
|
return dfResult;
|
|
}
|
|
|
|
#define OGR_FP(x) OGR_FetchParm( adfProjParms, anParmIds, (x), \
|
|
dfFromGreenwich )
|
|
|
|
/************************************************************************/
|
|
/* SetEPSGProjCS() */
|
|
/************************************************************************/
|
|
|
|
static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode )
|
|
|
|
{
|
|
int nGCSCode, nUOMAngleCode, nUOMLength, nTRFCode, nProjMethod=0;
|
|
int anParmIds[7], nCSC = 0;
|
|
char *pszPCSName = NULL, *pszUOMLengthName = NULL;
|
|
double adfProjParms[7], dfInMeters, dfFromGreenwich;
|
|
OGRErr nErr;
|
|
OGR_SRSNode *poNode;
|
|
|
|
if( !EPSGGetPCSInfo( nPCSCode, &pszPCSName, &nUOMLength, &nUOMAngleCode,
|
|
&nGCSCode, &nTRFCode, &nCSC ) )
|
|
{
|
|
CPLFree(pszPCSName);
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
poSRS->SetNode( "PROJCS", pszPCSName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set GEOGCS. */
|
|
/* -------------------------------------------------------------------- */
|
|
nErr = SetEPSGGeogCS( poSRS, nGCSCode );
|
|
if( nErr != OGRERR_NONE )
|
|
{
|
|
CPLFree(pszPCSName);
|
|
return nErr;
|
|
}
|
|
|
|
dfFromGreenwich = poSRS->GetPrimeMeridian();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set linear units. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !EPSGGetUOMLengthInfo( nUOMLength, &pszUOMLengthName, &dfInMeters ) )
|
|
{
|
|
CPLFree(pszPCSName);
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
poSRS->SetLinearUnits( pszUOMLengthName, dfInMeters );
|
|
poSRS->SetAuthority( "PROJCS|UNIT", "EPSG", nUOMLength );
|
|
|
|
CPLFree( pszUOMLengthName );
|
|
CPLFree( pszPCSName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set projection and parameters. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !EPSGGetProjTRFInfo( nPCSCode, &nProjMethod, anParmIds, adfProjParms ))
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
|
|
switch( nProjMethod )
|
|
{
|
|
case 9801:
|
|
case 9817: /* really LCC near conformal */
|
|
poSRS->SetLCC1SP( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( NatOriginScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9802:
|
|
poSRS->SetLCC( OGR_FP( StdParallel1Lat ), OGR_FP( StdParallel2Lat ),
|
|
OGR_FP( FalseOriginLat ), OGR_FP( FalseOriginLong ),
|
|
OGR_FP( FalseOriginEasting ),
|
|
OGR_FP( FalseOriginNorthing ));
|
|
break;
|
|
|
|
case 9803:
|
|
poSRS->SetLCCB( OGR_FP( StdParallel1Lat ), OGR_FP( StdParallel2Lat ),
|
|
OGR_FP( FalseOriginLat ), OGR_FP( FalseOriginLong ),
|
|
OGR_FP( FalseOriginEasting ),
|
|
OGR_FP( FalseOriginNorthing ));
|
|
break;
|
|
|
|
case 9805:
|
|
poSRS->SetMercator2SP( OGR_FP( StdParallel1Lat ),
|
|
OGR_FP( NatOriginLat ), OGR_FP(NatOriginLong),
|
|
OGR_FP( FalseEasting ), OGR_FP(FalseNorthing) );
|
|
|
|
break;
|
|
|
|
case 9804:
|
|
case 9841: /* Mercator 1SP (Spherical) */
|
|
case 1024: /* Google Mercator */
|
|
poSRS->SetMercator( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( NatOriginScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
|
|
if( nProjMethod == 1024 || nProjMethod == 9841 ) // override hack for google mercator.
|
|
{
|
|
poSRS->SetExtension( "PROJCS", "PROJ4",
|
|
"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" );
|
|
}
|
|
break;
|
|
|
|
case 9806:
|
|
poSRS->SetCS( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9807:
|
|
poSRS->SetTM( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( NatOriginScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9808:
|
|
poSRS->SetTMSO( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( NatOriginScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9809:
|
|
poSRS->SetOS( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( NatOriginScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9810:
|
|
poSRS->SetPS( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( NatOriginScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9811:
|
|
poSRS->SetNZMG( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9812:
|
|
case 9813:
|
|
poSRS->SetHOM( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ),
|
|
OGR_FP( Azimuth ),
|
|
OGR_FP( AngleRectifiedToSkewedGrid ),
|
|
OGR_FP( InitialLineScaleFactor ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
|
|
poNode = poSRS->GetAttrNode( "PROJECTION" )->GetChild( 0 );
|
|
if( nProjMethod == 9813 )
|
|
poNode->SetValue( SRS_PT_LABORDE_OBLIQUE_MERCATOR );
|
|
break;
|
|
|
|
case 9814:
|
|
/* NOTE: This is no longer used! Swiss Oblique Mercator gets
|
|
** implemented using 9815 instead.
|
|
*/
|
|
poSRS->SetSOC( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9815:
|
|
poSRS->SetHOMAC( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ),
|
|
OGR_FP( Azimuth ),
|
|
OGR_FP( AngleRectifiedToSkewedGrid ),
|
|
OGR_FP( InitialLineScaleFactor ),
|
|
OGR_FP( ProjCenterEasting ),
|
|
OGR_FP( ProjCenterNorthing ) );
|
|
break;
|
|
|
|
case 9816:
|
|
poSRS->SetTMG( OGR_FP( FalseOriginLat ), OGR_FP( FalseOriginLong ),
|
|
OGR_FP( FalseOriginEasting ),
|
|
OGR_FP( FalseOriginNorthing ) );
|
|
break;
|
|
|
|
case 9818:
|
|
poSRS->SetPolyconic( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 1041: /* used by EPSG:5514 */
|
|
case 9819:
|
|
{
|
|
double dfCenterLong = OGR_FP( ProjCenterLong );
|
|
|
|
if( dfCenterLong == 0.0 ) // See ticket #2559
|
|
dfCenterLong = OGR_FP( PolarLongOrigin );
|
|
|
|
double dfAzimuth = OGR_FP( CoLatConeAxis ); // See ticket #4223
|
|
if( dfAzimuth == 0.0 )
|
|
dfAzimuth = OGR_FP( Azimuth );
|
|
|
|
poSRS->SetKrovak( OGR_FP( ProjCenterLat ), dfCenterLong,
|
|
dfAzimuth,
|
|
OGR_FP( PseudoStdParallelLat ),
|
|
OGR_FP( PseudoStdParallelScaleFactor ),
|
|
OGR_FP( ProjCenterEasting ),
|
|
OGR_FP( ProjCenterNorthing ) );
|
|
}
|
|
break;
|
|
|
|
case 9820:
|
|
case 1027: /* used by EPSG:2163, 3408, 3409, 3973 and 3974 */
|
|
poSRS->SetLAEA( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9821: /* DEPREACTED : this is the spherical form, and really needs different
|
|
equations which give different results but PROJ.4 doesn't
|
|
seem to support the spherical form. */
|
|
poSRS->SetLAEA( OGR_FP( SphericalOriginLat ),
|
|
OGR_FP( SphericalOriginLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9822: /* Albers (Conic) Equal Area */
|
|
poSRS->SetACEA( OGR_FP( StdParallel1Lat ),
|
|
OGR_FP( StdParallel2Lat ),
|
|
OGR_FP( FalseOriginLat ),
|
|
OGR_FP( FalseOriginLong ),
|
|
OGR_FP( FalseOriginEasting ),
|
|
OGR_FP( FalseOriginNorthing ) );
|
|
break;
|
|
|
|
case 9823: /* Equidistant Cylindrical / Plate Carre / Equirectangular */
|
|
case 9842:
|
|
case 1028:
|
|
case 1029:
|
|
poSRS->SetEquirectangular( OGR_FP( NatOriginLat ),
|
|
OGR_FP( NatOriginLong ),
|
|
0.0, 0.0 );
|
|
break;
|
|
|
|
case 9829: /* Polar Stereographic (Variant B) */
|
|
poSRS->SetPS( OGR_FP( PolarLatStdParallel ), OGR_FP(PolarLongOrigin),
|
|
1.0,
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
case 9834: /* Lambert Cylindrical Equal Area (Spherical) bug #2659 */
|
|
poSRS->SetCEA( OGR_FP( StdParallel1Lat ), OGR_FP( NatOriginLong ),
|
|
OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) );
|
|
break;
|
|
|
|
default:
|
|
CPLDebug( "EPSG", "No WKT support for projection method %d.",
|
|
nProjMethod );
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set overall PCS authority code. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetAuthority( "PROJCS", "EPSG", nPCSCode );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set axes */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nCSC > 0 )
|
|
{
|
|
SetEPSGAxisInfo( poSRS, "PROJCS", nCSC );
|
|
CPLErrorReset();
|
|
}
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetEPSGVertCS() */
|
|
/************************************************************************/
|
|
|
|
static OGRErr SetEPSGVertCS( OGRSpatialReference * poSRS, int nVertCSCode )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch record from the vertcs.csv or override file. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszRecord;
|
|
char szSearchKey[24];
|
|
const char *pszFilename;
|
|
|
|
pszFilename = CSVFilename( "vertcs.override.csv" );
|
|
sprintf( szSearchKey, "%d", nVertCSCode );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
if( papszRecord == NULL )
|
|
{
|
|
pszFilename = CSVFilename( "vertcs.csv" );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
}
|
|
|
|
if( papszRecord == NULL )
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup the basic VERT_CS. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetVertCS(
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"COORD_REF_SYS_NAME")),
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"DATUM_NAME")) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should we add a geoidgrids extension node? */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszMethod =
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,"COORD_OP_METHOD_CODE_1"));
|
|
if( pszMethod && EQUAL(pszMethod,"9665") )
|
|
{
|
|
const char *pszParm11 =
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,"PARM_1_1"));
|
|
|
|
poSRS->SetExtension( "VERT_CS|VERT_DATUM", "PROJ4_GRIDS", pszParm11 );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup the VERT_DATUM node. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetAuthority( "VERT_CS|VERT_DATUM", "EPSG",
|
|
atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"DATUM_CODE"))) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set linear units. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszUOMLengthName = NULL;
|
|
double dfInMeters;
|
|
int nUOM_CODE = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"UOM_CODE")));
|
|
|
|
if( !EPSGGetUOMLengthInfo( nUOM_CODE, &pszUOMLengthName, &dfInMeters ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Failed to lookup UOM CODE %d", nUOM_CODE );
|
|
}
|
|
else
|
|
{
|
|
poSRS->SetTargetLinearUnits( "VERT_CS", pszUOMLengthName, dfInMeters );
|
|
poSRS->SetAuthority( "VERT_CS|UNIT", "EPSG", nUOM_CODE );
|
|
|
|
CPLFree( pszUOMLengthName );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set overall authority code. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetAuthority( "VERT_CS", "EPSG", nVertCSCode );
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetEPSGCompdCS() */
|
|
/************************************************************************/
|
|
|
|
static OGRErr SetEPSGCompdCS( OGRSpatialReference * poSRS, int nCCSCode )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch record from the compdcs.csv or override file. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszRecord = NULL;
|
|
char szSearchKey[24];
|
|
const char *pszFilename;
|
|
|
|
sprintf( szSearchKey, "%d", nCCSCode );
|
|
|
|
// So far no override file needed.
|
|
// pszFilename = CSVFilename( "compdcs.override.csv" );
|
|
// papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
// szSearchKey, CC_Integer );
|
|
|
|
//if( papszRecord == NULL )
|
|
{
|
|
pszFilename = CSVFilename( "compdcs.csv" );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
}
|
|
|
|
if( papszRecord == NULL )
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch subinformation now before anything messes with the */
|
|
/* last loaded record. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nPCSCode = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"CMPD_HORIZCRS_CODE")));
|
|
int nVertCSCode = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"CMPD_VERTCRS_CODE")));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the COMPD_CS node with a name. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetNode( "COMPD_CS",
|
|
CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"COORD_REF_SYS_NAME")) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Lookup the the projected coordinate system. Can the */
|
|
/* horizontal CRS be a GCS? */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRSpatialReference oPCS;
|
|
OGRErr eErr;
|
|
|
|
eErr = SetEPSGProjCS( &oPCS, nPCSCode );
|
|
if( eErr != OGRERR_NONE )
|
|
{
|
|
// perhaps it is a GCS?
|
|
eErr = SetEPSGGeogCS( &oPCS, nPCSCode );
|
|
}
|
|
|
|
if( eErr != OGRERR_NONE )
|
|
{
|
|
return eErr;
|
|
}
|
|
|
|
poSRS->GetRoot()->AddChild(
|
|
oPCS.GetRoot()->Clone() );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Lookup the VertCS. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRSpatialReference oVertCS;
|
|
eErr = SetEPSGVertCS( &oVertCS, nVertCSCode );
|
|
if( eErr != OGRERR_NONE )
|
|
return eErr;
|
|
|
|
poSRS->GetRoot()->AddChild(
|
|
oVertCS.GetRoot()->Clone() );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set overall authority code. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetAuthority( "COMPD_CS", "EPSG", nCCSCode );
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetEPSGGeocCS() */
|
|
/************************************************************************/
|
|
|
|
static OGRErr SetEPSGGeocCS( OGRSpatialReference * poSRS, int nGCSCode )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch record from the geoccs.csv or override file. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszRecord = NULL;
|
|
char szSearchKey[24];
|
|
const char *pszFilename;
|
|
|
|
sprintf( szSearchKey, "%d", nGCSCode );
|
|
|
|
// So far no override file needed.
|
|
// pszFilename = CSVFilename( "compdcs.override.csv" );
|
|
// papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
// szSearchKey, CC_Integer );
|
|
|
|
//if( papszRecord == NULL )
|
|
{
|
|
pszFilename = CSVFilename( "geoccs.csv" );
|
|
papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
|
|
szSearchKey, CC_Integer );
|
|
|
|
}
|
|
|
|
if( papszRecord == NULL )
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the GEOCCS node with a name. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->Clear();
|
|
poSRS->SetGeocCS( CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"COORD_REF_SYS_NAME")) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get datum related information. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nDatumCode, nEllipsoidCode, nPMCode;
|
|
char *pszDatumName;
|
|
|
|
nDatumCode = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"DATUM_CODE")));
|
|
|
|
pszDatumName =
|
|
CPLStrdup( CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,"DATUM_NAME") ) );
|
|
OGREPSGDatumNameMassage( &pszDatumName );
|
|
|
|
|
|
nEllipsoidCode = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"ELLIPSOID_CODE")));
|
|
|
|
nPMCode = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"PRIME_MERIDIAN_CODE")));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get prime meridian information. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszPMName = NULL;
|
|
double dfPMOffset = 0.0;
|
|
|
|
if( !EPSGGetPMInfo( nPMCode, &pszPMName, &dfPMOffset ) )
|
|
{
|
|
CPLFree( pszDatumName );
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the ellipsoid information. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszEllipsoidName = NULL;
|
|
double dfSemiMajor, dfInvFlattening;
|
|
|
|
if( OSRGetEllipsoidInfo( nEllipsoidCode, &pszEllipsoidName,
|
|
&dfSemiMajor, &dfInvFlattening ) != OGRERR_NONE )
|
|
{
|
|
CPLFree( pszDatumName );
|
|
CPLFree( pszPMName );
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup the spheroid. */
|
|
/* -------------------------------------------------------------------- */
|
|
char szValue[128];
|
|
|
|
OGR_SRSNode *poSpheroid = new OGR_SRSNode( "SPHEROID" );
|
|
poSpheroid->AddChild( new OGR_SRSNode( pszEllipsoidName ) );
|
|
|
|
OGRPrintDouble( szValue, dfSemiMajor );
|
|
poSpheroid->AddChild( new OGR_SRSNode(szValue) );
|
|
|
|
OGRPrintDouble( szValue, dfInvFlattening );
|
|
poSpheroid->AddChild( new OGR_SRSNode(szValue) );
|
|
|
|
CPLFree( pszEllipsoidName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup the Datum. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGR_SRSNode *poDatum = new OGR_SRSNode( "DATUM" );
|
|
poDatum->AddChild( new OGR_SRSNode(pszDatumName) );
|
|
poDatum->AddChild( poSpheroid );
|
|
|
|
poSRS->GetRoot()->AddChild( poDatum );
|
|
|
|
CPLFree( pszDatumName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup the prime meridian. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( dfPMOffset == 0.0 )
|
|
strcpy( szValue, "0" );
|
|
else
|
|
OGRPrintDouble( szValue, dfPMOffset );
|
|
|
|
OGR_SRSNode *poPM = new OGR_SRSNode( "PRIMEM" );
|
|
poPM->AddChild( new OGR_SRSNode( pszPMName ) );
|
|
poPM->AddChild( new OGR_SRSNode( szValue ) );
|
|
|
|
poSRS->GetRoot()->AddChild( poPM );
|
|
|
|
CPLFree( pszPMName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should we try to lookup a datum transform? */
|
|
/* -------------------------------------------------------------------- */
|
|
#ifdef notdef
|
|
if( EPSGGetWGS84Transform( nGeogCS, adfBursaTransform ) )
|
|
{
|
|
OGR_SRSNode *poWGS84;
|
|
char szValue[100];
|
|
|
|
poWGS84 = new OGR_SRSNode( "TOWGS84" );
|
|
|
|
for( int iCoeff = 0; iCoeff < 7; iCoeff++ )
|
|
{
|
|
CPLsprintf( szValue, "%g", adfBursaTransform[iCoeff] );
|
|
poWGS84->AddChild( new OGR_SRSNode( szValue ) );
|
|
}
|
|
|
|
poSRS->GetAttrNode( "DATUM" )->AddChild( poWGS84 );
|
|
}
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set linear units. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszUOMLengthName = NULL;
|
|
double dfInMeters = 1.0;
|
|
int nUOMLength = atoi(CSLGetField( papszRecord,
|
|
CSVGetFileFieldId(pszFilename,
|
|
"UOM_CODE")));
|
|
|
|
if( !EPSGGetUOMLengthInfo( nUOMLength, &pszUOMLengthName, &dfInMeters ) )
|
|
{
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
poSRS->SetLinearUnits( pszUOMLengthName, dfInMeters );
|
|
poSRS->SetAuthority( "GEOCCS|UNIT", "EPSG", nUOMLength );
|
|
|
|
CPLFree( pszUOMLengthName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set axes */
|
|
/* -------------------------------------------------------------------- */
|
|
OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" );
|
|
|
|
poAxis->AddChild( new OGR_SRSNode( "Geocentric X" ) );
|
|
poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_Other) ) );
|
|
|
|
poSRS->GetRoot()->AddChild( poAxis );
|
|
|
|
poAxis = new OGR_SRSNode( "AXIS" );
|
|
|
|
poAxis->AddChild( new OGR_SRSNode( "Geocentric Y" ) );
|
|
poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_Other) ) );
|
|
|
|
poSRS->GetRoot()->AddChild( poAxis );
|
|
|
|
poAxis = new OGR_SRSNode( "AXIS" );
|
|
|
|
poAxis->AddChild( new OGR_SRSNode( "Geocentric Z" ) );
|
|
poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_North) ) );
|
|
|
|
poSRS->GetRoot()->AddChild( poAxis );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the authority codes. */
|
|
/* -------------------------------------------------------------------- */
|
|
poSRS->SetAuthority( "DATUM", "EPSG", nDatumCode );
|
|
poSRS->SetAuthority( "SPHEROID", "EPSG", nEllipsoidCode );
|
|
poSRS->SetAuthority( "PRIMEM", "EPSG", nPMCode );
|
|
|
|
// if( nUOMAngle > 0 )
|
|
// poSRS->SetAuthority( "GEOGCS|UNIT", "EPSG", nUOMAngle );
|
|
|
|
poSRS->SetAuthority( "GEOCCS", "EPSG", nGCSCode );
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* importFromEPSG() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Initialize SRS based on EPSG GCS or PCS code.
|
|
*
|
|
* This method will initialize the spatial reference based on the
|
|
* passed in EPSG GCS or PCS code. The coordinate system definitions
|
|
* are normally read from the EPSG derived support files such as
|
|
* pcs.csv, gcs.csv, pcs.override.csv, gcs.override.csv and falling
|
|
* back to search for a PROJ.4 epsg init file or a definition in epsg.wkt.
|
|
*
|
|
* These support files are normally searched for in /usr/local/share/gdal
|
|
* or in the directory identified by the GDAL_DATA configuration option.
|
|
* See CPLFindFile() for details.
|
|
*
|
|
* This method is relatively expensive, and generally involves quite a bit
|
|
* of text file scanning. Reasonable efforts should be made to avoid calling
|
|
* it many times for the same coordinate system.
|
|
*
|
|
* This method is similar to importFromEPSGA() except that EPSG preferred
|
|
* axis ordering will *not* be applied for geographic coordinate systems.
|
|
* EPSG normally defines geographic coordinate systems to use lat/long
|
|
* contrary to typical GIS use). Since OGR 1.10.0, EPSG preferred
|
|
* axis ordering will also *not* be applied for projected coordinate systems
|
|
* that use northing/easting order.
|
|
*
|
|
* This method is the same as the C function OSRImportFromEPSG().
|
|
*
|
|
* @param nCode a GCS or PCS code from the horizontal coordinate system table.
|
|
*
|
|
* @return OGRERR_NONE on success, or an error code on failure.
|
|
*/
|
|
|
|
OGRErr OGRSpatialReference::importFromEPSG( int nCode )
|
|
|
|
{
|
|
OGRErr eErr = importFromEPSGA( nCode );
|
|
|
|
// Strip any GCS axis settings found.
|
|
if( eErr == OGRERR_NONE )
|
|
{
|
|
OGR_SRSNode *poGEOGCS = GetAttrNode( "GEOGCS" );
|
|
|
|
if( poGEOGCS != NULL )
|
|
poGEOGCS->StripNodes( "AXIS" );
|
|
|
|
OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" );
|
|
if (poPROJCS != NULL && EPSGTreatsAsNorthingEasting())
|
|
poPROJCS->StripNodes( "AXIS" );
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSRImportFromEPSG() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Initialize SRS based on EPSG GCS or PCS code.
|
|
*
|
|
* This function is the same as OGRSpatialReference::importFromEPSG().
|
|
*/
|
|
|
|
OGRErr CPL_STDCALL OSRImportFromEPSG( OGRSpatialReferenceH hSRS, int nCode )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSRImportFromEPSG", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->importFromEPSG( nCode );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* importFromEPSGA() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Initialize SRS based on EPSG GCS or PCS code.
|
|
*
|
|
* This method will initialize the spatial reference based on the
|
|
* passed in EPSG GCS or PCS code.
|
|
*
|
|
* This method is similar to importFromEPSG() except that EPSG preferred
|
|
* axis ordering *will* be applied for geographic and projected coordinate systems.
|
|
* EPSG normally defines geographic coordinate systems to use lat/long, and
|
|
* also there are also a few projected coordinate systems that use northing/easting
|
|
* order contrary to typical GIS use). See OGRSpatialReference::importFromEPSG()
|
|
* for more details on operation of this method.
|
|
*
|
|
* This method is the same as the C function OSRImportFromEPSGA().
|
|
*
|
|
* @param nCode a GCS or PCS code from the horizontal coordinate system table.
|
|
*
|
|
* @return OGRERR_NONE on success, or an error code on failure.
|
|
*/
|
|
|
|
OGRErr OGRSpatialReference::importFromEPSGA( int nCode )
|
|
|
|
{
|
|
OGRErr eErr;
|
|
|
|
bNormInfoSet = FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Clear any existing definition. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( GetRoot() != NULL )
|
|
{
|
|
delete poRoot;
|
|
poRoot = NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that we can find the required filename(s). */
|
|
/* -------------------------------------------------------------------- */
|
|
if( CSVScanFileByName( CSVFilename( "gcs.csv" ),
|
|
"COORD_REF_SYS_CODE",
|
|
"4269", CC_Integer ) == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OpenFailed,
|
|
"Unable to open EPSG support file %s.\n"
|
|
"Try setting the GDAL_DATA environment variable to point to the\n"
|
|
"directory containing EPSG csv files.",
|
|
CSVFilename( "gcs.csv" ) );
|
|
return OGRERR_FAILURE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try this as various sorts of objects till one works. */
|
|
/* -------------------------------------------------------------------- */
|
|
eErr = SetEPSGGeogCS( this, nCode );
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
eErr = SetEPSGProjCS( this, nCode );
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
eErr = SetEPSGVertCS( this, nCode );
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
eErr = SetEPSGCompdCS( this, nCode );
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
eErr = SetEPSGGeocCS( this, nCode );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we get it as an unsupported code, try looking it up in */
|
|
/* the epsg.wkt coordinate system dictionary. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
{
|
|
char szCode[32];
|
|
sprintf( szCode, "%d", nCode );
|
|
eErr = importFromDict( "epsg.wkt", szCode );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we get it as an unsupported code, try looking it up in */
|
|
/* the PROJ.4 support file(s). */
|
|
/* -------------------------------------------------------------------- */
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
{
|
|
char szWrkDefn[100];
|
|
char *pszNormalized;
|
|
|
|
sprintf( szWrkDefn, "+init=epsg:%d", nCode );
|
|
|
|
pszNormalized = OCTProj4Normalize( szWrkDefn );
|
|
|
|
if( strstr(pszNormalized,"proj=") != NULL )
|
|
eErr = importFromProj4( pszNormalized );
|
|
|
|
CPLFree( pszNormalized );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Push in authority information if we were successful, and it */
|
|
/* is not already present. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszAuthName;
|
|
|
|
if( IsProjected() )
|
|
pszAuthName = GetAuthorityName( "PROJCS" );
|
|
else
|
|
pszAuthName = GetAuthorityName( "GEOGCS" );
|
|
|
|
|
|
if( eErr == OGRERR_NONE && pszAuthName == NULL )
|
|
{
|
|
if( IsProjected() )
|
|
SetAuthority( "PROJCS", "EPSG", nCode );
|
|
else if( IsGeographic() )
|
|
SetAuthority( "GEOGCS", "EPSG", nCode );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Otherwise officially issue an error message. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( eErr == OGRERR_UNSUPPORTED_SRS )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"EPSG PCS/GCS code %d not found in EPSG support files. Is this a valid\nEPSG coordinate system?",
|
|
nCode );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* To the extent possible, we want to return the results in as */
|
|
/* close to standard OGC format as possible, so we fixup the */
|
|
/* ordering. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( eErr == OGRERR_NONE )
|
|
{
|
|
eErr = FixupOrdering();
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSRImportFromEPSGA() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Initialize SRS based on EPSG GCS or PCS code.
|
|
*
|
|
* This function is the same as OGRSpatialReference::importFromEPSGA().
|
|
*/
|
|
|
|
OGRErr CPL_STDCALL OSRImportFromEPSGA( OGRSpatialReferenceH hSRS, int nCode )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSRImportFromEPSGA", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->importFromEPSGA( nCode );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetStatePlane() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Set State Plane projection definition.
|
|
*
|
|
* This will attempt to generate a complete definition of a state plane
|
|
* zone based on generating the entire SRS from the EPSG tables. If the
|
|
* EPSG tables are unavailable, it will produce a stubbed LOCAL_CS definition
|
|
* and return OGRERR_FAILURE.
|
|
*
|
|
* This method is the same as the C function OSRSetStatePlaneWithUnits().
|
|
*
|
|
* @param nZone State plane zone number, in the USGS numbering scheme (as
|
|
* dinstinct from the Arc/Info and Erdas numbering scheme.
|
|
*
|
|
* @param bNAD83 TRUE if the NAD83 zone definition should be used or FALSE
|
|
* if the NAD27 zone definition should be used.
|
|
*
|
|
* @param pszOverrideUnitName Linear unit name to apply overriding the
|
|
* legal definition for this zone.
|
|
*
|
|
* @param dfOverrideUnit Linear unit conversion factor to apply overriding
|
|
* the legal definition for this zone.
|
|
*
|
|
* @return OGRERR_NONE on success, or OGRERR_FAILURE on failure, mostly likely
|
|
* due to the EPSG tables not being accessable.
|
|
*/
|
|
|
|
OGRErr OGRSpatialReference::SetStatePlane( int nZone, int bNAD83,
|
|
const char *pszOverrideUnitName,
|
|
double dfOverrideUnit )
|
|
|
|
{
|
|
int nAdjustedId;
|
|
int nPCSCode;
|
|
char szID[32];
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the index id from stateplane.csv. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bNAD83 )
|
|
nAdjustedId = nZone;
|
|
else
|
|
nAdjustedId = nZone + 10000;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Turn this into a PCS code. We assume there will only be one */
|
|
/* PCS corresponding to each Proj_ code since the proj code */
|
|
/* already effectively indicates NAD27 or NAD83. */
|
|
/* -------------------------------------------------------------------- */
|
|
sprintf( szID, "%d", nAdjustedId );
|
|
nPCSCode =
|
|
atoi( CSVGetField( CSVFilename( "stateplane.csv" ),
|
|
"ID", szID, CC_Integer,
|
|
"EPSG_PCS_CODE" ) );
|
|
if( nPCSCode < 1 )
|
|
{
|
|
char szName[128];
|
|
static int bFailureReported = FALSE;
|
|
|
|
if( !bFailureReported )
|
|
{
|
|
bFailureReported = TRUE;
|
|
CPLError( CE_Warning, CPLE_OpenFailed,
|
|
"Unable to find state plane zone in stateplane.csv,\n"
|
|
"likely because the GDAL data files cannot be found. Using\n"
|
|
"incomplete definition of state plane zone.\n" );
|
|
}
|
|
|
|
Clear();
|
|
if( bNAD83 )
|
|
{
|
|
sprintf( szName, "State Plane Zone %d / NAD83", nZone );
|
|
SetLocalCS( szName );
|
|
SetLinearUnits( SRS_UL_METER, 1.0 );
|
|
}
|
|
else
|
|
{
|
|
sprintf( szName, "State Plane Zone %d / NAD27", nZone );
|
|
SetLocalCS( szName );
|
|
SetLinearUnits( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) );
|
|
}
|
|
|
|
return OGRERR_FAILURE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Define based on a full EPSG definition of the zone. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRErr eErr = importFromEPSG( nPCSCode );
|
|
|
|
if( eErr != OGRERR_NONE )
|
|
return eErr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply units override if required. */
|
|
/* */
|
|
/* We will need to adjust the linear projection parameter to */
|
|
/* match the provided units, and clear the authority code. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( dfOverrideUnit != 0.0
|
|
&& fabs(dfOverrideUnit - GetLinearUnits()) > 0.0000000001 )
|
|
{
|
|
double dfFalseEasting = GetNormProjParm( SRS_PP_FALSE_EASTING );
|
|
double dfFalseNorthing= GetNormProjParm( SRS_PP_FALSE_NORTHING);
|
|
OGR_SRSNode *poPROJCS;
|
|
|
|
SetLinearUnits( pszOverrideUnitName, dfOverrideUnit );
|
|
|
|
SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
|
|
SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
|
|
|
|
poPROJCS = GetAttrNode( "PROJCS" );
|
|
if( poPROJCS != NULL && poPROJCS->FindChild( "AUTHORITY" ) != -1 )
|
|
{
|
|
poPROJCS->DestroyChild( poPROJCS->FindChild( "AUTHORITY" ) );
|
|
}
|
|
}
|
|
|
|
return OGRERR_NONE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSRSetStatePlane() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Set State Plane projection definition.
|
|
*
|
|
* This function is the same as OGRSpatialReference::SetStatePlane().
|
|
*/
|
|
|
|
OGRErr OSRSetStatePlane( OGRSpatialReferenceH hSRS, int nZone, int bNAD83 )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSRSetStatePlane", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->SetStatePlane( nZone, bNAD83 );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSRSetStatePlaneWithUnits() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Set State Plane projection definition.
|
|
*
|
|
* This function is the same as OGRSpatialReference::SetStatePlane().
|
|
*/
|
|
|
|
OGRErr OSRSetStatePlaneWithUnits( OGRSpatialReferenceH hSRS,
|
|
int nZone, int bNAD83,
|
|
const char *pszOverrideUnitName,
|
|
double dfOverrideUnit )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSRSetStatePlaneWithUnits", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->SetStatePlane( nZone, bNAD83,
|
|
pszOverrideUnitName,
|
|
dfOverrideUnit );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetEPSGGeogCS() */
|
|
/* */
|
|
/* Try to establish what the EPSG code for this coordinate */
|
|
/* systems GEOGCS might be. Returns -1 if no reasonable guess */
|
|
/* can be made. */
|
|
/* */
|
|
/* TODO: We really need to do some name lookups. */
|
|
/************************************************************************/
|
|
|
|
int OGRSpatialReference::GetEPSGGeogCS()
|
|
|
|
{
|
|
const char *pszAuthName = GetAuthorityName( "GEOGCS" );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we already have it? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
|
|
return atoi(GetAuthorityCode( "GEOGCS" ));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the datum and geogcs names. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszGEOGCS = GetAttrValue( "GEOGCS" );
|
|
const char *pszDatum = GetAttrValue( "DATUM" );
|
|
|
|
// We can only operate on coordinate systems with a geogcs.
|
|
if( pszGEOGCS == NULL || pszDatum == NULL )
|
|
return -1;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Is this a "well known" geographic coordinate system? */
|
|
/* -------------------------------------------------------------------- */
|
|
int bWGS, bNAD;
|
|
|
|
bWGS = strstr(pszGEOGCS,"WGS") != NULL
|
|
|| strstr(pszDatum, "WGS")
|
|
|| strstr(pszGEOGCS,"World Geodetic System")
|
|
|| strstr(pszGEOGCS,"World_Geodetic_System")
|
|
|| strstr(pszDatum, "World Geodetic System")
|
|
|| strstr(pszDatum, "World_Geodetic_System");
|
|
|
|
bNAD = strstr(pszGEOGCS,"NAD") != NULL
|
|
|| strstr(pszDatum, "NAD")
|
|
|| strstr(pszGEOGCS,"North American")
|
|
|| strstr(pszGEOGCS,"North_American")
|
|
|| strstr(pszDatum, "North American")
|
|
|| strstr(pszDatum, "North_American");
|
|
|
|
if( bWGS && (strstr(pszGEOGCS,"84") || strstr(pszDatum,"84")) )
|
|
return 4326;
|
|
|
|
if( bWGS && (strstr(pszGEOGCS,"72") || strstr(pszDatum,"72")) )
|
|
return 4322;
|
|
|
|
if( bNAD && (strstr(pszGEOGCS,"83") || strstr(pszDatum,"83")) )
|
|
return 4269;
|
|
|
|
if( bNAD && (strstr(pszGEOGCS,"27") || strstr(pszDatum,"27")) )
|
|
return 4267;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we know the datum, associate the most likely GCS with */
|
|
/* it. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszAuthName = GetAuthorityName( "GEOGCS|DATUM" );
|
|
|
|
if( pszAuthName != NULL
|
|
&& EQUAL(pszAuthName,"epsg")
|
|
&& GetPrimeMeridian() == 0.0 )
|
|
{
|
|
int nDatum = atoi(GetAuthorityCode("GEOGCS|DATUM"));
|
|
|
|
if( nDatum >= 6000 && nDatum <= 6999 )
|
|
return nDatum - 2000;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* AutoIdentifyEPSG() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Set EPSG authority info if possible.
|
|
*
|
|
* This method inspects a WKT definition, and adds EPSG authority nodes
|
|
* where an aspect of the coordinate system can be easily and safely
|
|
* corresponded with an EPSG identifier. In practice, this method will
|
|
* evolve over time. In theory it can add authority nodes for any object
|
|
* (ie. spheroid, datum, GEOGCS, units, and PROJCS) that could have an
|
|
* authority node. Mostly this is useful to inserting appropriate
|
|
* PROJCS codes for common formulations (like UTM n WGS84).
|
|
*
|
|
* If it success the OGRSpatialReference is updated in place, and the
|
|
* method return OGRERR_NONE. If the method fails to identify the
|
|
* general coordinate system OGRERR_UNSUPPORTED_SRS is returned but no
|
|
* error message is posted via CPLError().
|
|
*
|
|
* This method is the same as the C function OSRAutoIdentifyEPSG().
|
|
*
|
|
* @return OGRERR_NONE or OGRERR_UNSUPPORTED_SRS.
|
|
*/
|
|
|
|
OGRErr OGRSpatialReference::AutoIdentifyEPSG()
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a GEOGCS node, but no authority? If so, try */
|
|
/* guessing it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( (IsProjected() || IsGeographic())
|
|
&& GetAuthorityCode( "GEOGCS" ) == NULL )
|
|
{
|
|
int nGCS = GetEPSGGeogCS();
|
|
if( nGCS != -1 )
|
|
SetAuthority( "GEOGCS", "EPSG", nGCS );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Is this a UTM coordinate system with a common GEOGCS? */
|
|
/* -------------------------------------------------------------------- */
|
|
int nZone, bNorth;
|
|
if( (nZone = GetUTMZone( &bNorth )) != 0
|
|
&& GetAuthorityCode( "PROJCS") == NULL )
|
|
{
|
|
const char *pszAuthName, *pszAuthCode;
|
|
|
|
pszAuthName = GetAuthorityName( "PROJCS|GEOGCS" );
|
|
pszAuthCode = GetAuthorityCode( "PROJCS|GEOGCS" );
|
|
|
|
if( pszAuthName == NULL || pszAuthCode == NULL )
|
|
{
|
|
/* don't exactly recognise datum */
|
|
}
|
|
else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4326 )
|
|
{ // WGS84
|
|
if( bNorth )
|
|
SetAuthority( "PROJCS", "EPSG", 32600 + nZone );
|
|
else
|
|
SetAuthority( "PROJCS", "EPSG", 32700 + nZone );
|
|
}
|
|
else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4267
|
|
&& nZone >= 3 && nZone <= 22 && bNorth )
|
|
SetAuthority( "PROJCS", "EPSG", 26700 + nZone ); // NAD27
|
|
else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4269
|
|
&& nZone >= 3 && nZone <= 23 && bNorth )
|
|
SetAuthority( "PROJCS", "EPSG", 26900 + nZone ); // NAD83
|
|
else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4322 )
|
|
{ // WGS72
|
|
if( bNorth )
|
|
SetAuthority( "PROJCS", "EPSG", 32200 + nZone );
|
|
else
|
|
SetAuthority( "PROJCS", "EPSG", 32300 + nZone );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Return. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( IsProjected() && GetAuthorityCode("PROJCS") != NULL )
|
|
return OGRERR_NONE;
|
|
else if( IsGeographic() && GetAuthorityCode("GEOGCS") != NULL )
|
|
return OGRERR_NONE;
|
|
else
|
|
return OGRERR_UNSUPPORTED_SRS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSRAutoIdentifyEPSG() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Set EPSG authority info if possible.
|
|
*
|
|
* This function is the same as OGRSpatialReference::AutoIdentifyEPSG().
|
|
*/
|
|
|
|
OGRErr OSRAutoIdentifyEPSG( OGRSpatialReferenceH hSRS )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSRAutoIdentifyEPSG", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->AutoIdentifyEPSG();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGTreatsAsLatLong() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief This method returns TRUE if EPSG feels this geographic coordinate
|
|
* system should be treated as having lat/long coordinate ordering.
|
|
*
|
|
* Currently this returns TRUE for all geographic coordinate systems
|
|
* with an EPSG code set, and AXIS values set defining it as lat, long.
|
|
* Note that coordinate systems with an EPSG code and no axis settings
|
|
* will be assumed to not be lat/long.
|
|
*
|
|
* FALSE will be returned for all coordinate systems that are not geographic,
|
|
* or that do not have an EPSG code set.
|
|
*
|
|
* This method is the same as the C function OSREPSGTreatsAsLatLong().
|
|
*
|
|
* @return TRUE or FALSE.
|
|
*/
|
|
|
|
int OGRSpatialReference::EPSGTreatsAsLatLong()
|
|
|
|
{
|
|
if( !IsGeographic() )
|
|
return FALSE;
|
|
|
|
const char *pszAuth = GetAuthorityName( "GEOGCS" );
|
|
|
|
if( pszAuth == NULL || !EQUAL(pszAuth,"EPSG") )
|
|
return FALSE;
|
|
|
|
OGR_SRSNode *poFirstAxis = GetAttrNode( "GEOGCS|AXIS" );
|
|
|
|
if( poFirstAxis == NULL )
|
|
return FALSE;
|
|
|
|
if( poFirstAxis->GetChildCount() >= 2
|
|
&& EQUAL(poFirstAxis->GetChild(1)->GetValue(),"NORTH") )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSREPSGTreatsAsLatLong() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief This function returns TRUE if EPSG feels this geographic coordinate
|
|
* system should be treated as having lat/long coordinate ordering.
|
|
*
|
|
* This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
|
|
*/
|
|
|
|
int OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsLatLong", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->EPSGTreatsAsLatLong();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* EPSGTreatsAsNorthingEasting() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief This method returns TRUE if EPSG feels this projected coordinate
|
|
* system should be treated as having northing/easting coordinate ordering.
|
|
*
|
|
* Currently this returns TRUE for all projected coordinate systems
|
|
* with an EPSG code set, and AXIS values set defining it as northing, easting.
|
|
*
|
|
* FALSE will be returned for all coordinate systems that are not projected,
|
|
* or that do not have an EPSG code set.
|
|
*
|
|
* This method is the same as the C function EPSGTreatsAsNorthingEasting().
|
|
*
|
|
* @return TRUE or FALSE.
|
|
*
|
|
* @since OGR 1.10.0
|
|
*/
|
|
|
|
int OGRSpatialReference::EPSGTreatsAsNorthingEasting()
|
|
|
|
{
|
|
if( !IsProjected() )
|
|
return FALSE;
|
|
|
|
const char *pszAuth = GetAuthorityName( "PROJCS" );
|
|
|
|
if( pszAuth == NULL || !EQUAL(pszAuth,"EPSG") )
|
|
return FALSE;
|
|
|
|
OGR_SRSNode *poFirstAxis = GetAttrNode( "PROJCS|AXIS" );
|
|
|
|
if( poFirstAxis == NULL )
|
|
return FALSE;
|
|
|
|
if( poFirstAxis->GetChildCount() >= 2
|
|
&& EQUAL(poFirstAxis->GetChild(1)->GetValue(),"NORTH") )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OSREPSGTreatsAsNorthingEasting() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief This function returns TRUE if EPSG feels this geographic coordinate
|
|
* system should be treated as having northing/easting coordinate ordering.
|
|
*
|
|
* This function is the same as OGRSpatialReference::EPSGTreatsAsNorthingEasting().
|
|
*
|
|
* @since OGR 1.10.0
|
|
*/
|
|
|
|
int OSREPSGTreatsAsNorthingEasting( OGRSpatialReferenceH hSRS )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsNorthingEasting", CE_Failure );
|
|
|
|
return ((OGRSpatialReference *) hSRS)->EPSGTreatsAsNorthingEasting();
|
|
}
|