Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Command line application to list info about a given CRS.
5 : * Outputs a number of formats (WKT, PROJ.4, etc.).
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : * Etienne Tourigny, etourigny.dev-at-gmail-dot-com
8 : *
9 : * ****************************************************************************
10 : * Copyright (c) 1998, Frank Warmerdam
11 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include "cpl_string.h"
17 : #include "gdal_version.h"
18 : #include "gdal_priv.h"
19 : #include "ogr_spatialref.h"
20 : #include "ogr_api.h"
21 : #include "ogrsf_frmts.h"
22 : #include "commonutils.h"
23 :
24 : #include "proj.h"
25 :
26 : bool FindSRS(const char *pszInput, OGRSpatialReference &oSRS);
27 : CPLErr PrintSRS(const OGRSpatialReference &oSRS, const char *pszOutputType,
28 : bool bPretty, bool bPrintSep);
29 : void PrintSRSOutputTypes(const OGRSpatialReference &oSRS,
30 : const char *const *papszOutputTypes, bool bPretty);
31 :
32 : /************************************************************************/
33 : /* Usage() */
34 : /************************************************************************/
35 :
36 0 : static void Usage(bool bIsError, const char *pszErrorMsg = nullptr)
37 :
38 : {
39 0 : fprintf(bIsError ? stderr : stdout,
40 : "Usage: gdalsrsinfo [options] <srs_def>\n"
41 : "\n"
42 : "srs_def may be the filename of a dataset supported by GDAL/OGR "
43 : "from which to extract SRS information\n"
44 : "OR any of the usual GDAL/OGR forms "
45 : "(complete WKT, PROJ.4, EPSG:n or a file containing the SRS)\n"
46 : "\n"
47 : "Options: \n"
48 : " [--help-general] Show help on general options and exit\n"
49 : " [--help] [-h] Show help and exit\n"
50 : " [--single-line] Print WKT on single line\n"
51 : " [-V] Validate SRS\n"
52 : " [-e] Search for EPSG number(s) corresponding "
53 : "to SRS\n"
54 : " [-o <out_type>] Output type { default, all, wkt_all,\n"
55 : #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 2
56 : " PROJJSON, proj4, epsg,\n"
57 : #else
58 : " proj4, epsg,\n"
59 : #endif
60 : " wkt1, wkt_simple, "
61 : "wkt_noct, wkt_esri,\n"
62 : " wkt2, wkt2_2015, "
63 : "wkt2_2019, mapinfo, xml }\n\n");
64 :
65 0 : if (pszErrorMsg != nullptr)
66 0 : fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);
67 :
68 0 : exit(bIsError ? 1 : 0);
69 : }
70 :
71 : /************************************************************************/
72 : /* main() */
73 : /************************************************************************/
74 :
75 : #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
76 : do \
77 : { \
78 : if (i + nExtraArg >= argc) \
79 : Usage(true, CPLSPrintf("%s option requires %d argument(s)", \
80 : argv[i], nExtraArg)); \
81 : } while (false)
82 :
83 21 : MAIN_START(argc, argv)
84 :
85 : {
86 21 : bool bGotSRS = false;
87 21 : bool bPretty = true;
88 21 : bool bValidate = false;
89 21 : bool bFindEPSG = false;
90 40 : std::string osIdentifiedCode = "EPSG:-1";
91 21 : const char *pszInput = nullptr;
92 21 : const char *pszOutputType = "default";
93 40 : OGRSpatialReference oSRS;
94 :
95 : /* Check strict compilation and runtime library version as we use C++ API */
96 21 : if (!GDAL_CHECK_VERSION(argv[0]))
97 0 : exit(1);
98 :
99 21 : EarlySetConfigOptions(argc, argv);
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Register standard GDAL and OGR drivers. */
103 : /* -------------------------------------------------------------------- */
104 21 : GDALAllRegister();
105 :
106 : /* -------------------------------------------------------------------- */
107 : /* Register standard GDAL drivers, and process generic GDAL */
108 : /* command options. */
109 : /* -------------------------------------------------------------------- */
110 21 : argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
111 21 : if (argc < 1)
112 1 : exit(-argc);
113 :
114 : /* -------------------------------------------------------------------- */
115 : /* Parse arguments. */
116 : /* -------------------------------------------------------------------- */
117 65 : for (int i = 1; i < argc; i++)
118 : {
119 45 : CPLDebug("gdalsrsinfo", "got arg #%d : [%s]", i, argv[i]);
120 :
121 45 : if (EQUAL(argv[i], "--utility_version"))
122 : {
123 0 : printf("%s was compiled against GDAL %s and is running against "
124 : "GDAL %s\n",
125 : argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
126 0 : CSLDestroy(argv);
127 0 : return 0;
128 : }
129 45 : else if (EQUAL(argv[i], "-h") || EQUAL(argv[i], "--help"))
130 0 : Usage(false);
131 45 : else if (EQUAL(argv[i], "-e"))
132 3 : bFindEPSG = true;
133 42 : else if (EQUAL(argv[i], "-o"))
134 : {
135 14 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
136 14 : pszOutputType = argv[++i];
137 : }
138 28 : else if (EQUAL(argv[i], "-p"))
139 0 : bPretty = true;
140 28 : else if (EQUAL(argv[i], "--single-line"))
141 6 : bPretty = false;
142 22 : else if (EQUAL(argv[i], "-V"))
143 2 : bValidate = true;
144 20 : else if (argv[i][0] == '-')
145 : {
146 0 : Usage(true, CPLSPrintf("Unknown option name '%s'", argv[i]));
147 : }
148 : else
149 20 : pszInput = argv[i];
150 : }
151 :
152 20 : if (pszInput == nullptr)
153 : {
154 0 : CSLDestroy(argv);
155 0 : Usage(true, "No input specified.");
156 : }
157 :
158 : /* Search for SRS */
159 : /* coverity[tainted_data] */
160 20 : bGotSRS = FindSRS(pszInput, oSRS) == TRUE;
161 :
162 20 : CPLDebug("gdalsrsinfo",
163 : "bGotSRS: %d bValidate: %d pszOutputType: %s bPretty: %d",
164 : static_cast<int>(bGotSRS), static_cast<int>(bValidate),
165 : pszOutputType, static_cast<int>(bPretty));
166 :
167 : /* Make sure we got a SRS */
168 20 : if (!bGotSRS)
169 : {
170 1 : CPLError(CE_Failure, CPLE_AppDefined,
171 : "ERROR - failed to load SRS definition from %s", pszInput);
172 1 : exit(1);
173 : }
174 :
175 : else
176 : {
177 19 : int nEntries = 0;
178 19 : int *panConfidence = nullptr;
179 19 : OGRSpatialReferenceH *pahSRS = nullptr;
180 :
181 : /* Find EPSG code */
182 19 : if (EQUAL(pszOutputType, "epsg"))
183 0 : bFindEPSG = true;
184 :
185 19 : if (bFindEPSG)
186 : {
187 :
188 : pahSRS =
189 3 : OSRFindMatches(reinterpret_cast<OGRSpatialReferenceH>(
190 : const_cast<OGRSpatialReference *>(&oSRS)),
191 : nullptr, &nEntries, &panConfidence);
192 : }
193 :
194 38 : for (int i = 0; i < (nEntries ? nEntries : 1); i++)
195 : {
196 19 : if (nEntries)
197 : {
198 2 : oSRS = *reinterpret_cast<OGRSpatialReference *>(pahSRS[i]);
199 2 : if (panConfidence[i] != 100)
200 : {
201 1 : printf("Confidence in this match: %d %%\n",
202 1 : panConfidence[i]);
203 : }
204 :
205 2 : const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
206 2 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
207 2 : if (pszAuthorityName && pszAuthorityCode)
208 : {
209 2 : osIdentifiedCode = pszAuthorityName;
210 2 : osIdentifiedCode += ':';
211 2 : osIdentifiedCode += pszAuthorityCode;
212 : }
213 : }
214 :
215 : /* Validate - not well tested!*/
216 19 : if (bValidate)
217 : {
218 2 : OGRErr eErr = oSRS.Validate();
219 2 : if (eErr != OGRERR_NONE)
220 : {
221 1 : printf("\nValidate Fails");
222 1 : if (eErr == OGRERR_CORRUPT_DATA)
223 1 : printf(" - SRS is not well formed");
224 0 : else if (eErr == OGRERR_UNSUPPORTED_SRS)
225 0 : printf(" - contains non-standard PROJECTION[] values");
226 1 : printf("\n");
227 : }
228 : else
229 1 : printf("\nValidate Succeeds\n");
230 : }
231 :
232 : /* Output */
233 19 : if (EQUAL("default", pszOutputType))
234 : {
235 5 : const char *papszOutputTypes[] = {"proj4", "wkt2", nullptr};
236 5 : if (bFindEPSG)
237 3 : printf("\n%s\n", osIdentifiedCode.c_str());
238 5 : PrintSRSOutputTypes(oSRS, papszOutputTypes, bPretty);
239 : }
240 14 : else if (EQUAL("all", pszOutputType))
241 : {
242 1 : if (bFindEPSG)
243 0 : printf("\n%s\n", osIdentifiedCode.c_str());
244 1 : const char *papszOutputTypes[] = {
245 : "proj4", "wkt1", "wkt2_2015",
246 : "wkt2_2019", "wkt_simple", "wkt_noct",
247 : "wkt_esri", "mapinfo", "xml",
248 : #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 2
249 : "PROJJSON",
250 : #endif
251 : nullptr};
252 1 : PrintSRSOutputTypes(oSRS, papszOutputTypes, bPretty);
253 : }
254 13 : else if (EQUAL("wkt_all", pszOutputType))
255 : {
256 0 : const char *papszOutputTypes[] = {
257 : "wkt1", "wkt2_2015", "wkt2_2019", "wkt_simple",
258 : "wkt_noct", "wkt_esri", nullptr};
259 0 : PrintSRSOutputTypes(oSRS, papszOutputTypes, bPretty);
260 : }
261 : else
262 : {
263 13 : if (bPretty)
264 7 : printf("\n");
265 13 : if (EQUAL(pszOutputType, "epsg"))
266 0 : printf("\n%s\n", osIdentifiedCode.c_str());
267 : else
268 13 : PrintSRS(oSRS, pszOutputType, bPretty, FALSE);
269 13 : if (bPretty)
270 7 : printf("\n");
271 : }
272 : }
273 :
274 19 : OSRFreeSRSArray(pahSRS);
275 19 : CPLFree(panConfidence);
276 : }
277 :
278 : /* cleanup anything left */
279 19 : GDALDestroyDriverManager();
280 19 : OGRCleanupAll();
281 19 : CSLDestroy(argv);
282 :
283 19 : return 0;
284 : }
285 :
286 0 : MAIN_END
287 :
288 : /************************************************************************/
289 : /* FindSRS() */
290 : /* */
291 : /* Search for SRS from pszInput, update oSRS. */
292 : /************************************************************************/
293 20 : bool FindSRS(const char *pszInput, OGRSpatialReference &oSRS)
294 :
295 : {
296 20 : bool bGotSRS = false;
297 20 : GDALDataset *poGDALDS = nullptr;
298 20 : OGRLayer *poLayer = nullptr;
299 20 : bool bIsFile = false;
300 :
301 : /* temporarily suppress error messages we may get from xOpen() */
302 20 : bool bDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
303 20 : if (!bDebug)
304 20 : CPLPushErrorHandler(CPLQuietErrorHandler);
305 :
306 : /* Test if argument is a file */
307 20 : VSILFILE *fp = VSIFOpenL(pszInput, "r");
308 20 : if (fp)
309 : {
310 13 : bIsFile = true;
311 13 : VSIFCloseL(fp);
312 13 : CPLDebug("gdalsrsinfo", "argument is a file");
313 : }
314 :
315 : /* try to open with GDAL */
316 20 : if (!STARTS_WITH(pszInput, "http://spatialreference.org/"))
317 : {
318 20 : CPLDebug("gdalsrsinfo", "trying to open with GDAL");
319 : poGDALDS = static_cast<GDALDataset *>(
320 20 : GDALOpenEx(pszInput, 0, nullptr, nullptr, nullptr));
321 : }
322 20 : if (poGDALDS != nullptr)
323 : {
324 11 : const OGRSpatialReference *poSRS = poGDALDS->GetSpatialRef();
325 11 : if (poSRS)
326 : {
327 11 : oSRS = *poSRS;
328 11 : CPLDebug("gdalsrsinfo", "got SRS from GDAL");
329 11 : bGotSRS = true;
330 : }
331 0 : else if (poGDALDS->GetLayerCount() > 0)
332 : {
333 0 : poLayer = poGDALDS->GetLayer(0);
334 0 : if (poLayer != nullptr)
335 : {
336 0 : poSRS = poLayer->GetSpatialRef();
337 0 : if (poSRS != nullptr)
338 : {
339 0 : CPLDebug("gdalsrsinfo", "got SRS from OGR");
340 0 : bGotSRS = true;
341 0 : oSRS = *poSRS;
342 : }
343 : }
344 : }
345 11 : GDALClose(poGDALDS);
346 11 : if (!bGotSRS)
347 0 : CPLDebug("gdalsrsinfo", "did not open with GDAL");
348 : }
349 :
350 : /* Try ESRI file */
351 20 : if (!bGotSRS && bIsFile && (strstr(pszInput, ".prj") != nullptr))
352 : {
353 2 : CPLDebug("gdalsrsinfo", "trying to get SRS from ESRI .prj file [%s]",
354 : pszInput);
355 :
356 : char **pszTemp;
357 2 : if (strstr(pszInput, "ESRI::") != nullptr)
358 0 : pszTemp = CSLLoad(pszInput + 6);
359 : else
360 2 : pszTemp = CSLLoad(pszInput);
361 :
362 2 : OGRErr eErr = OGRERR_UNSUPPORTED_SRS;
363 2 : if (pszTemp)
364 : {
365 2 : eErr = oSRS.importFromESRI(pszTemp);
366 2 : CSLDestroy(pszTemp);
367 : }
368 :
369 2 : if (eErr != OGRERR_NONE)
370 : {
371 0 : CPLDebug("gdalsrsinfo", "did not get SRS from ESRI .prj file");
372 : }
373 : else
374 : {
375 2 : CPLDebug("gdalsrsinfo", "got SRS from ESRI .prj file");
376 2 : bGotSRS = true;
377 : }
378 : }
379 :
380 : /* restore error messages */
381 20 : if (!bDebug)
382 20 : CPLPopErrorHandler();
383 :
384 : /* Last resort, try OSRSetFromUserInput() */
385 20 : if (!bGotSRS)
386 : {
387 7 : CPLDebug("gdalsrsinfo", "trying to get SRS from user input [%s]",
388 : pszInput);
389 :
390 7 : if (CPLGetConfigOption("CPL_ALLOW_VSISTDIN", nullptr) == nullptr)
391 7 : CPLSetConfigOption("CPL_ALLOW_VSISTDIN", "YES");
392 :
393 7 : const OGRErr eErr = oSRS.SetFromUserInput(pszInput);
394 :
395 7 : if (eErr != OGRERR_NONE)
396 : {
397 1 : CPLDebug("gdalsrsinfo", "did not get SRS from user input");
398 : }
399 : else
400 : {
401 6 : CPLDebug("gdalsrsinfo", "got SRS from user input");
402 6 : bGotSRS = true;
403 : }
404 : }
405 :
406 20 : return bGotSRS;
407 : }
408 :
409 : /************************************************************************/
410 : /* PrintSRS() */
411 : /* */
412 : /* Print spatial reference in specified format. */
413 : /************************************************************************/
414 33 : CPLErr PrintSRS(const OGRSpatialReference &oSRS, const char *pszOutputType,
415 : bool bPretty, bool bPrintSep)
416 :
417 : {
418 33 : if (!pszOutputType || EQUAL(pszOutputType, ""))
419 0 : return CE_None;
420 :
421 33 : CPLDebug("gdalsrsinfo", "PrintSRS( oSRS, %s, %d, %d )\n", pszOutputType,
422 : static_cast<int>(bPretty), static_cast<int>(bPrintSep));
423 :
424 33 : char *pszOutput = nullptr;
425 :
426 33 : if (EQUAL("proj4", pszOutputType))
427 : {
428 12 : if (bPrintSep)
429 6 : printf("PROJ.4 : ");
430 12 : oSRS.exportToProj4(&pszOutput);
431 12 : printf("%s\n", pszOutput ? pszOutput : "(error)");
432 : }
433 :
434 21 : else if (EQUAL("PROJJSON", pszOutputType))
435 : {
436 1 : if (bPrintSep)
437 1 : printf("PROJJSON :\n");
438 1 : const char *const apszOptions[] = {
439 1 : bPretty ? "MULTILINE=YES" : "MULTILINE=NO", nullptr};
440 1 : oSRS.exportToPROJJSON(&pszOutput, apszOptions);
441 1 : printf("%s\n", pszOutput ? pszOutput : "(error)");
442 : }
443 :
444 20 : else if (EQUAL("wkt1", pszOutputType))
445 : {
446 4 : if (bPrintSep)
447 1 : printf("OGC WKT1 :\n");
448 4 : const char *const apszOptions[] = {
449 4 : "FORMAT=WKT1_GDAL", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
450 4 : oSRS.exportToWkt(&pszOutput, apszOptions);
451 4 : printf("%s\n", pszOutput ? pszOutput : "(error)");
452 : }
453 :
454 16 : else if (EQUAL("wkt_simple", pszOutputType))
455 : {
456 2 : if (bPrintSep)
457 1 : printf("OGC WKT1 (simple) :\n");
458 2 : const char *const apszOptions[] = {
459 2 : "FORMAT=WKT1_SIMPLE", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
460 2 : oSRS.exportToWkt(&pszOutput, apszOptions);
461 2 : printf("%s\n", pszOutput ? pszOutput : "(error)");
462 : }
463 :
464 14 : else if (EQUAL("wkt_noct", pszOutputType))
465 : {
466 2 : if (bPrintSep)
467 1 : printf("OGC WKT1 (no CT) :\n");
468 2 : const char *const apszOptions[] = {
469 2 : "FORMAT=SFSQL", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
470 2 : oSRS.exportToWkt(&pszOutput, apszOptions);
471 2 : printf("%s\n", pszOutput ? pszOutput : "(error)");
472 : }
473 :
474 12 : else if (EQUAL("wkt_esri", pszOutputType))
475 : {
476 2 : if (bPrintSep)
477 1 : printf("ESRI WKT :\n");
478 2 : const char *const apszOptions[] = {
479 2 : "FORMAT=WKT1_ESRI", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
480 2 : oSRS.exportToWkt(&pszOutput, apszOptions);
481 2 : printf("%s\n", pszOutput ? pszOutput : "(error)");
482 : }
483 :
484 10 : else if (EQUAL("wkt2_2015", pszOutputType))
485 : {
486 1 : if (bPrintSep)
487 1 : printf("OGC WKT2:2015 :\n");
488 1 : const char *const apszOptions[] = {
489 1 : "FORMAT=WKT2_2015", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
490 1 : oSRS.exportToWkt(&pszOutput, apszOptions);
491 1 : printf("%s\n", pszOutput ? pszOutput : "(error)");
492 : }
493 :
494 9 : else if (EQUAL("wkt", pszOutputType) || EQUAL("wkt2", pszOutputType) ||
495 4 : EQUAL("wkt2_2018", pszOutputType) ||
496 4 : EQUAL("wkt2_2019", pszOutputType))
497 : {
498 6 : if (bPrintSep)
499 6 : printf("OGC WKT2:2019 :\n");
500 6 : const char *const apszOptions[] = {
501 6 : "FORMAT=WKT2_2018", bPretty ? "MULTILINE=YES" : nullptr, nullptr};
502 6 : oSRS.exportToWkt(&pszOutput, apszOptions);
503 6 : printf("%s\n", pszOutput ? pszOutput : "(error)");
504 : }
505 :
506 3 : else if (EQUAL("mapinfo", pszOutputType))
507 : {
508 2 : if (bPrintSep)
509 1 : printf("MAPINFO : ");
510 2 : oSRS.exportToMICoordSys(&pszOutput);
511 2 : printf("\'%s\'\n", pszOutput ? pszOutput : "(error)");
512 : }
513 :
514 1 : else if (EQUAL("xml", pszOutputType))
515 : {
516 1 : if (bPrintSep)
517 1 : printf("XML :\n");
518 1 : oSRS.exportToXML(&pszOutput, nullptr);
519 1 : printf("%s\n", pszOutput ? pszOutput : "(error)");
520 : }
521 :
522 : else
523 : {
524 0 : CPLError(CE_Failure, CPLE_AppDefined, "ERROR - %s output not supported",
525 : pszOutputType);
526 0 : return CE_Failure;
527 : }
528 :
529 33 : CPLFree(pszOutput);
530 :
531 33 : return CE_None;
532 : }
533 :
534 : /************************************************************************/
535 : /* PrintSRSOutputTypes() */
536 : /* */
537 : /* Print spatial reference in specified formats. */
538 : /************************************************************************/
539 6 : void PrintSRSOutputTypes(const OGRSpatialReference &oSRS,
540 : const char *const *papszOutputTypes, bool bPretty)
541 :
542 : {
543 6 : int nOutputTypes = CSLCount(papszOutputTypes);
544 6 : printf("\n");
545 26 : for (int i = 0; i < nOutputTypes; i++)
546 : {
547 20 : PrintSRS(oSRS, papszOutputTypes[i], bPretty, true);
548 20 : printf("\n");
549 : }
550 6 : }
|