558 lines
19 KiB
C++
558 lines
19 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: GDAL Utilities
|
|
* Purpose: Command line application to list info about a given CRS.
|
|
* Outputs a number of formats (WKT, PROJ.4, etc.).
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
* Etienne Tourigny, etourigny.dev-at-gmail-dot-com
|
|
*
|
|
* ****************************************************************************
|
|
* Copyright (c) 1998, Frank Warmerdam
|
|
* Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
****************************************************************************/
|
|
|
|
#include "cpl_string.h"
|
|
#include "gdal_version.h"
|
|
#include "gdal_priv.h"
|
|
#include "ogr_spatialref.h"
|
|
#include "ogr_api.h"
|
|
#include "ogrsf_frmts.h"
|
|
#include "commonutils.h"
|
|
|
|
#include "proj.h"
|
|
|
|
bool FindSRS(const char *pszInput, OGRSpatialReference &oSRS);
|
|
CPLErr PrintSRS(const OGRSpatialReference &oSRS, const char *pszOutputType,
|
|
bool bPretty, bool bPrintSep);
|
|
void PrintSRSOutputTypes(const OGRSpatialReference &oSRS,
|
|
const char *const *papszOutputTypes, bool bPretty);
|
|
|
|
/************************************************************************/
|
|
/* Usage() */
|
|
/************************************************************************/
|
|
|
|
static void Usage(bool bIsError, const char *pszErrorMsg = nullptr)
|
|
|
|
{
|
|
fprintf(bIsError ? stderr : stdout,
|
|
"Usage: gdalsrsinfo [options] <srs_def>\n"
|
|
"\n"
|
|
"srs_def may be the filename of a dataset supported by GDAL/OGR "
|
|
"from which to extract SRS information\n"
|
|
"OR any of the usual GDAL/OGR forms "
|
|
"(complete WKT, PROJ.4, EPSG:n or a file containing the SRS)\n"
|
|
"\n"
|
|
"Options: \n"
|
|
" [--help-general] Show help on general options and exit\n"
|
|
" [--help] [-h] Show help and exit\n"
|
|
" [--single-line] Print WKT on single line\n"
|
|
" [-V] Validate SRS\n"
|
|
" [-e] Search for EPSG number(s) corresponding "
|
|
"to SRS\n"
|
|
" [-o <out_type>] Output type { default, all, wkt_all,\n"
|
|
#if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 2
|
|
" PROJJSON, proj4, epsg,\n"
|
|
#else
|
|
" proj4, epsg,\n"
|
|
#endif
|
|
" wkt1, wkt_simple, "
|
|
"wkt_noct, wkt_esri,\n"
|
|
" wkt2, wkt2_2015, "
|
|
"wkt2_2019, mapinfo, xml }\n\n");
|
|
|
|
if (pszErrorMsg != nullptr)
|
|
fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);
|
|
|
|
exit(bIsError ? 1 : 0);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* main() */
|
|
/************************************************************************/
|
|
|
|
#define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
|
|
do \
|
|
{ \
|
|
if (i + nExtraArg >= argc) \
|
|
Usage(true, CPLSPrintf("%s option requires %d argument(s)", \
|
|
argv[i], nExtraArg)); \
|
|
} while (false)
|
|
|
|
MAIN_START(argc, argv)
|
|
|
|
{
|
|
bool bGotSRS = false;
|
|
bool bPretty = true;
|
|
bool bValidate = false;
|
|
bool bFindEPSG = false;
|
|
std::string osIdentifiedCode = "EPSG:-1";
|
|
const char *pszInput = nullptr;
|
|
const char *pszOutputType = "default";
|
|
OGRSpatialReference oSRS;
|
|
|
|
/* Check strict compilation and runtime library version as we use C++ API */
|
|
if (!GDAL_CHECK_VERSION(argv[0]))
|
|
exit(1);
|
|
|
|
EarlySetConfigOptions(argc, argv);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Register standard GDAL and OGR drivers. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALAllRegister();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Register standard GDAL drivers, and process generic GDAL */
|
|
/* command options. */
|
|
/* -------------------------------------------------------------------- */
|
|
argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
|
|
if (argc < 1)
|
|
exit(-argc);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Parse arguments. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
CPLDebug("gdalsrsinfo", "got arg #%d : [%s]", i, argv[i]);
|
|
|
|
if (EQUAL(argv[i], "--utility_version"))
|
|
{
|
|
printf("%s was compiled against GDAL %s and is running against "
|
|
"GDAL %s\n",
|
|
argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
|
|
CSLDestroy(argv);
|
|
return 0;
|
|
}
|
|
else if (EQUAL(argv[i], "-h") || EQUAL(argv[i], "--help"))
|
|
Usage(false);
|
|
else if (EQUAL(argv[i], "-e"))
|
|
bFindEPSG = true;
|
|
else if (EQUAL(argv[i], "-o"))
|
|
{
|
|
CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
|
|
pszOutputType = argv[++i];
|
|
}
|
|
else if (EQUAL(argv[i], "-p"))
|
|
bPretty = true;
|
|
else if (EQUAL(argv[i], "--single-line"))
|
|
bPretty = false;
|
|
else if (EQUAL(argv[i], "-V"))
|
|
bValidate = true;
|
|
else if (argv[i][0] == '-')
|
|
{
|
|
Usage(true, CPLSPrintf("Unknown option name '%s'", argv[i]));
|
|
}
|
|
else
|
|
pszInput = argv[i];
|
|
}
|
|
|
|
if (pszInput == nullptr)
|
|
{
|
|
CSLDestroy(argv);
|
|
Usage(true, "No input specified.");
|
|
}
|
|
|
|
/* Search for SRS */
|
|
/* coverity[tainted_data] */
|
|
bGotSRS = FindSRS(pszInput, oSRS) == TRUE;
|
|
|
|
CPLDebug("gdalsrsinfo",
|
|
"bGotSRS: %d bValidate: %d pszOutputType: %s bPretty: %d",
|
|
static_cast<int>(bGotSRS), static_cast<int>(bValidate),
|
|
pszOutputType, static_cast<int>(bPretty));
|
|
|
|
/* Make sure we got a SRS */
|
|
if (!bGotSRS)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"ERROR - failed to load SRS definition from %s", pszInput);
|
|
exit(1);
|
|
}
|
|
|
|
else
|
|
{
|
|
int nEntries = 0;
|
|
int *panConfidence = nullptr;
|
|
OGRSpatialReferenceH *pahSRS = nullptr;
|
|
|
|
/* Find EPSG code */
|
|
if (EQUAL(pszOutputType, "epsg"))
|
|
bFindEPSG = true;
|
|
|
|
if (bFindEPSG)
|
|
{
|
|
|
|
pahSRS =
|
|
OSRFindMatches(reinterpret_cast<OGRSpatialReferenceH>(
|
|
const_cast<OGRSpatialReference *>(&oSRS)),
|
|
nullptr, &nEntries, &panConfidence);
|
|
}
|
|
|
|
for (int i = 0; i < (nEntries ? nEntries : 1); i++)
|
|
{
|
|
if (nEntries)
|
|
{
|
|
oSRS = *reinterpret_cast<OGRSpatialReference *>(pahSRS[i]);
|
|
if (panConfidence[i] != 100)
|
|
{
|
|
printf("Confidence in this match: %d %%\n",
|
|
panConfidence[i]);
|
|
}
|
|
|
|
const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
|
|
const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
|
|
if (pszAuthorityName && pszAuthorityCode)
|
|
{
|
|
osIdentifiedCode = pszAuthorityName;
|
|
osIdentifiedCode += ':';
|
|
osIdentifiedCode += pszAuthorityCode;
|
|
}
|
|
}
|
|
|
|
/* Validate - not well tested!*/
|
|
if (bValidate)
|
|
{
|
|
OGRErr eErr = oSRS.Validate();
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("\nValidate Fails");
|
|
if (eErr == OGRERR_CORRUPT_DATA)
|
|
printf(" - SRS is not well formed");
|
|
else if (eErr == OGRERR_UNSUPPORTED_SRS)
|
|
printf(" - contains non-standard PROJECTION[] values");
|
|
printf("\n");
|
|
}
|
|
else
|
|
printf("\nValidate Succeeds\n");
|
|
}
|
|
|
|
/* Output */
|
|
if (EQUAL("default", pszOutputType))
|
|
{
|
|
const char *papszOutputTypes[] = {"proj4", "wkt2", nullptr};
|
|
if (bFindEPSG)
|
|
printf("\n%s\n", osIdentifiedCode.c_str());
|
|
PrintSRSOutputTypes(oSRS, papszOutputTypes, bPretty);
|
|
}
|
|
else if (EQUAL("all", pszOutputType))
|
|
{
|
|
if (bFindEPSG)
|
|
printf("\n%s\n", osIdentifiedCode.c_str());
|
|
const char *papszOutputTypes[] = {
|
|
"proj4",
|
|
"wkt1",
|
|
"wkt2_2015",
|
|
"wkt2_2019",
|
|
"wkt_simple",
|
|
"wkt_noct",
|
|
"wkt_esri",
|
|
"mapinfo",
|
|
"xml",
|
|
#if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 2
|
|
"PROJJSON",
|
|
#endif
|
|
nullptr
|
|
};
|
|
PrintSRSOutputTypes(oSRS, papszOutputTypes, bPretty);
|
|
}
|
|
else if (EQUAL("wkt_all", pszOutputType))
|
|
{
|
|
const char *papszOutputTypes[] = {
|
|
"wkt1", "wkt2_2015", "wkt2_2019", "wkt_simple",
|
|
"wkt_noct", "wkt_esri", nullptr};
|
|
PrintSRSOutputTypes(oSRS, papszOutputTypes, bPretty);
|
|
}
|
|
else
|
|
{
|
|
if (bPretty)
|
|
printf("\n");
|
|
if (EQUAL(pszOutputType, "epsg"))
|
|
printf("\n%s\n", osIdentifiedCode.c_str());
|
|
else
|
|
PrintSRS(oSRS, pszOutputType, bPretty, FALSE);
|
|
if (bPretty)
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
OSRFreeSRSArray(pahSRS);
|
|
CPLFree(panConfidence);
|
|
}
|
|
|
|
/* cleanup anything left */
|
|
GDALDestroyDriverManager();
|
|
OGRCleanupAll();
|
|
CSLDestroy(argv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
MAIN_END
|
|
|
|
/************************************************************************/
|
|
/* FindSRS() */
|
|
/* */
|
|
/* Search for SRS from pszInput, update oSRS. */
|
|
/************************************************************************/
|
|
bool FindSRS(const char *pszInput, OGRSpatialReference &oSRS)
|
|
|
|
{
|
|
bool bGotSRS = false;
|
|
GDALDataset *poGDALDS = nullptr;
|
|
OGRLayer *poLayer = nullptr;
|
|
bool bIsFile = false;
|
|
|
|
/* temporarily suppress error messages we may get from xOpen() */
|
|
bool bDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
|
|
if (!bDebug)
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
|
|
/* Test if argument is a file */
|
|
VSILFILE *fp = VSIFOpenL(pszInput, "r");
|
|
if (fp)
|
|
{
|
|
bIsFile = true;
|
|
VSIFCloseL(fp);
|
|
CPLDebug("gdalsrsinfo", "argument is a file");
|
|
}
|
|
|
|
/* try to open with GDAL */
|
|
if (!STARTS_WITH(pszInput, "http://spatialreference.org/"))
|
|
{
|
|
CPLDebug("gdalsrsinfo", "trying to open with GDAL");
|
|
poGDALDS = static_cast<GDALDataset *>(
|
|
GDALOpenEx(pszInput, 0, nullptr, nullptr, nullptr));
|
|
}
|
|
if (poGDALDS != nullptr)
|
|
{
|
|
const OGRSpatialReference *poSRS = poGDALDS->GetSpatialRef();
|
|
if (poSRS)
|
|
{
|
|
oSRS = *poSRS;
|
|
CPLDebug("gdalsrsinfo", "got SRS from GDAL");
|
|
bGotSRS = true;
|
|
}
|
|
else if (poGDALDS->GetLayerCount() > 0)
|
|
{
|
|
poLayer = poGDALDS->GetLayer(0);
|
|
if (poLayer != nullptr)
|
|
{
|
|
poSRS = poLayer->GetSpatialRef();
|
|
if (poSRS != nullptr)
|
|
{
|
|
CPLDebug("gdalsrsinfo", "got SRS from OGR");
|
|
bGotSRS = true;
|
|
oSRS = *poSRS;
|
|
}
|
|
}
|
|
}
|
|
GDALClose(poGDALDS);
|
|
if (!bGotSRS)
|
|
CPLDebug("gdalsrsinfo", "did not open with GDAL");
|
|
}
|
|
|
|
/* Try ESRI file */
|
|
if (!bGotSRS && bIsFile && (strstr(pszInput, ".prj") != nullptr))
|
|
{
|
|
CPLDebug("gdalsrsinfo", "trying to get SRS from ESRI .prj file [%s]",
|
|
pszInput);
|
|
|
|
char **pszTemp;
|
|
if (strstr(pszInput, "ESRI::") != nullptr)
|
|
pszTemp = CSLLoad(pszInput + 6);
|
|
else
|
|
pszTemp = CSLLoad(pszInput);
|
|
|
|
OGRErr eErr = OGRERR_UNSUPPORTED_SRS;
|
|
if (pszTemp)
|
|
{
|
|
eErr = oSRS.importFromESRI(pszTemp);
|
|
CSLDestroy(pszTemp);
|
|
}
|
|
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
CPLDebug("gdalsrsinfo", "did not get SRS from ESRI .prj file");
|
|
}
|
|
else
|
|
{
|
|
CPLDebug("gdalsrsinfo", "got SRS from ESRI .prj file");
|
|
bGotSRS = true;
|
|
}
|
|
}
|
|
|
|
/* restore error messages */
|
|
if (!bDebug)
|
|
CPLPopErrorHandler();
|
|
|
|
/* Last resort, try OSRSetFromUserInput() */
|
|
if (!bGotSRS)
|
|
{
|
|
CPLDebug("gdalsrsinfo", "trying to get SRS from user input [%s]",
|
|
pszInput);
|
|
|
|
if (CPLGetConfigOption("CPL_ALLOW_VSISTDIN", nullptr) == nullptr)
|
|
CPLSetConfigOption("CPL_ALLOW_VSISTDIN", "YES");
|
|
|
|
const OGRErr eErr = oSRS.SetFromUserInput(pszInput);
|
|
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
CPLDebug("gdalsrsinfo", "did not get SRS from user input");
|
|
}
|
|
else
|
|
{
|
|
CPLDebug("gdalsrsinfo", "got SRS from user input");
|
|
bGotSRS = true;
|
|
}
|
|
}
|
|
|
|
return bGotSRS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PrintSRS() */
|
|
/* */
|
|
/* Print spatial reference in specified format. */
|
|
/************************************************************************/
|
|
CPLErr PrintSRS(const OGRSpatialReference &oSRS, const char *pszOutputType,
|
|
bool bPretty, bool bPrintSep)
|
|
|
|
{
|
|
if (!pszOutputType || EQUAL(pszOutputType, ""))
|
|
return CE_None;
|
|
|
|
CPLDebug("gdalsrsinfo", "PrintSRS( oSRS, %s, %d, %d )\n", pszOutputType,
|
|
static_cast<int>(bPretty), static_cast<int>(bPrintSep));
|
|
|
|
char *pszOutput = nullptr;
|
|
|
|
if (EQUAL("proj4", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("PROJ.4 : ");
|
|
oSRS.exportToProj4(&pszOutput);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("PROJJSON", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("PROJJSON :\n");
|
|
const char *const apszOptions[] = {
|
|
bPretty ? "MULTILINE=YES" : "MULTILINE=NO", nullptr};
|
|
oSRS.exportToPROJJSON(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("wkt1", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("OGC WKT1 :\n");
|
|
const char *const apszOptions[] = {
|
|
"FORMAT=WKT1_GDAL", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
|
|
oSRS.exportToWkt(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("wkt_simple", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("OGC WKT1 (simple) :\n");
|
|
const char *const apszOptions[] = {
|
|
"FORMAT=WKT1_SIMPLE", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
|
|
oSRS.exportToWkt(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("wkt_noct", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("OGC WKT1 (no CT) :\n");
|
|
const char *const apszOptions[] = {
|
|
"FORMAT=SFSQL", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
|
|
oSRS.exportToWkt(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("wkt_esri", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("ESRI WKT :\n");
|
|
const char *const apszOptions[] = {
|
|
"FORMAT=WKT1_ESRI", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
|
|
oSRS.exportToWkt(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("wkt2_2015", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("OGC WKT2:2015 :\n");
|
|
const char *const apszOptions[] = {
|
|
"FORMAT=WKT2_2015", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
|
|
oSRS.exportToWkt(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("wkt", pszOutputType) || EQUAL("wkt2", pszOutputType) ||
|
|
EQUAL("wkt2_2018", pszOutputType) ||
|
|
EQUAL("wkt2_2019", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("OGC WKT2:2019 :\n");
|
|
const char *const apszOptions[] = {
|
|
"FORMAT=WKT2_2018", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
|
|
oSRS.exportToWkt(&pszOutput, apszOptions);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("mapinfo", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("MAPINFO : ");
|
|
oSRS.exportToMICoordSys(&pszOutput);
|
|
printf("\'%s\'\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else if (EQUAL("xml", pszOutputType))
|
|
{
|
|
if (bPrintSep)
|
|
printf("XML :\n");
|
|
oSRS.exportToXML(&pszOutput, nullptr);
|
|
printf("%s\n", pszOutput ? pszOutput : "(error)");
|
|
}
|
|
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "ERROR - %s output not supported",
|
|
pszOutputType);
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLFree(pszOutput);
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PrintSRSOutputTypes() */
|
|
/* */
|
|
/* Print spatial reference in specified formats. */
|
|
/************************************************************************/
|
|
void PrintSRSOutputTypes(const OGRSpatialReference &oSRS,
|
|
const char *const *papszOutputTypes, bool bPretty)
|
|
|
|
{
|
|
int nOutputTypes = CSLCount(papszOutputTypes);
|
|
printf("\n");
|
|
for (int i = 0; i < nOutputTypes; i++)
|
|
{
|
|
PrintSRS(oSRS, papszOutputTypes[i], bPretty, true);
|
|
printf("\n");
|
|
}
|
|
}
|