Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Command line point transformer.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 :
16 : #include <cstdio>
17 : #include <cstdlib>
18 : #include <cctype>
19 :
20 : #include "cpl_conv.h"
21 : #include "cpl_error.h"
22 : #include "cpl_string.h"
23 : #include "gdal_version.h"
24 : #include "gdal_alg.h"
25 : #include "gdalwarper.h"
26 : #include "gdal.h"
27 : #include "ogr_api.h"
28 : #include "ogr_core.h"
29 : #include "ogr_spatialref.h"
30 : #include "ogr_srs_api.h"
31 : #include "commonutils.h"
32 :
33 : /************************************************************************/
34 : /* Usage() */
35 : /************************************************************************/
36 :
37 0 : static void Usage(bool bIsError, const char *pszErrorMsg = nullptr)
38 :
39 : {
40 0 : fprintf(bIsError ? stderr : stdout,
41 : "Usage: gdaltransform [--help] [--help-general]\n"
42 : " [-i] [-s_srs <srs_def>] [-t_srs <srs_def>] [-to "
43 : ">NAME>=<VALUE>]...\n"
44 : " [-s_coord_epoch <epoch>] [-t_coord_epoch <epoch>]\n"
45 : " [-ct <proj_string>] [-order <n>] [-tps] [-rpc] [-geoloc] \n"
46 : " [-gcp <pixel> <line> <easting> <northing> [elevation]]...\n"
47 : " [-output_xy] [-E] [-field_sep <sep>] [-ignore_extra_input]\n"
48 : " [<srcfile> [<dstfile>]]\n"
49 : "\n");
50 :
51 0 : if (pszErrorMsg != nullptr)
52 0 : fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);
53 :
54 0 : exit(bIsError ? 1 : 0);
55 : }
56 :
57 : /************************************************************************/
58 : /* IsValidSRS */
59 : /************************************************************************/
60 :
61 8 : static bool IsValidSRS(const char *pszUserInput)
62 :
63 : {
64 : OGRSpatialReferenceH hSRS;
65 8 : bool bRes = true;
66 :
67 8 : CPLErrorReset();
68 :
69 8 : hSRS = OSRNewSpatialReference(nullptr);
70 8 : if (OSRSetFromUserInput(hSRS, pszUserInput) != OGRERR_NONE)
71 : {
72 0 : bRes = false;
73 0 : CPLError(CE_Failure, CPLE_AppDefined,
74 : "Translating source or target SRS failed:\n%s", pszUserInput);
75 : }
76 :
77 8 : OSRDestroySpatialReference(hSRS);
78 :
79 8 : return bRes;
80 : }
81 :
82 : /************************************************************************/
83 : /* main() */
84 : /************************************************************************/
85 :
86 : #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
87 : do \
88 : { \
89 : if (i + nExtraArg >= argc) \
90 : Usage(true, CPLSPrintf("%s option requires %d argument(s)", \
91 : argv[i], nExtraArg)); \
92 : } while (false)
93 :
94 15 : MAIN_START(argc, argv)
95 :
96 : {
97 : // Check that we are running against at least GDAL 1.5.
98 : // Note to developers: if we use newer API, please change the requirement.
99 15 : if (atoi(GDALVersionInfo("VERSION_NUM")) < 1500)
100 : {
101 0 : fprintf(stderr,
102 : "At least, GDAL >= 1.5.0 is required for this version of %s, "
103 : "which was compiled against GDAL %s\n",
104 : argv[0], GDAL_RELEASE_NAME);
105 0 : exit(1);
106 : }
107 :
108 15 : GDALAllRegister();
109 15 : argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
110 15 : if (argc < 1)
111 0 : exit(-argc);
112 :
113 15 : const char *pszSrcFilename = nullptr;
114 15 : const char *pszDstFilename = nullptr;
115 15 : int nOrder = 0;
116 : void *hTransformArg;
117 15 : GDALTransformerFunc pfnTransformer = nullptr;
118 15 : int nGCPCount = 0;
119 15 : GDAL_GCP *pasGCPs = nullptr;
120 15 : int bInverse = FALSE;
121 30 : CPLStringList aosTO;
122 15 : int bOutputXY = FALSE;
123 15 : double dfX = 0.0;
124 15 : double dfY = 0.0;
125 15 : double dfZ = 0.0;
126 15 : double dfT = 0.0;
127 15 : bool bCoordOnCommandLine = false;
128 15 : bool bIgnoreExtraInput = false;
129 15 : bool bEchoInput = false;
130 30 : std::string osFieldSep = " ";
131 :
132 : /* -------------------------------------------------------------------- */
133 : /* Parse arguments. */
134 : /* -------------------------------------------------------------------- */
135 54 : for (int i = 1; i < argc && argv[i] != nullptr; i++)
136 : {
137 40 : if (EQUAL(argv[i], "--utility_version"))
138 : {
139 1 : printf("%s was compiled against GDAL %s and "
140 : "is running against GDAL %s\n",
141 : argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
142 1 : CSLDestroy(argv);
143 1 : return 0;
144 : }
145 39 : else if (EQUAL(argv[i], "--help"))
146 : {
147 0 : Usage(false);
148 : }
149 39 : else if (EQUAL(argv[i], "-t_srs"))
150 : {
151 5 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
152 5 : const char *pszSRS = argv[++i];
153 5 : if (!IsValidSRS(pszSRS))
154 0 : exit(1);
155 5 : aosTO.SetNameValue("DST_SRS", pszSRS);
156 : }
157 34 : else if (EQUAL(argv[i], "-s_srs"))
158 : {
159 3 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
160 3 : const char *pszSRS = argv[++i];
161 : // coverity[tainted_data]
162 3 : if (!IsValidSRS(pszSRS))
163 0 : exit(1);
164 3 : aosTO.SetNameValue("SRC_SRS", pszSRS);
165 : }
166 31 : else if (EQUAL(argv[i], "-s_coord_epoch"))
167 : {
168 0 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
169 0 : const char *pszCoordinateEpoch = argv[++i];
170 0 : aosTO.SetNameValue("SRC_COORDINATE_EPOCH", pszCoordinateEpoch);
171 : }
172 31 : else if (EQUAL(argv[i], "-t_coord_epoch"))
173 : {
174 0 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
175 0 : const char *pszCoordinateEpoch = argv[++i];
176 0 : aosTO.SetNameValue("DST_COORDINATE_EPOCH", pszCoordinateEpoch);
177 : }
178 31 : else if (EQUAL(argv[i], "-ct"))
179 : {
180 1 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
181 1 : const char *pszCT = argv[++i];
182 1 : aosTO.SetNameValue("COORDINATE_OPERATION", pszCT);
183 : }
184 30 : else if (EQUAL(argv[i], "-order"))
185 : {
186 1 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
187 1 : nOrder = atoi(argv[++i]);
188 1 : aosTO.SetNameValue("MAX_GCP_ORDER", argv[i]);
189 : }
190 29 : else if (EQUAL(argv[i], "-tps"))
191 : {
192 1 : aosTO.SetNameValue("METHOD", "GCP_TPS");
193 1 : nOrder = -1;
194 : }
195 28 : else if (EQUAL(argv[i], "-rpc"))
196 : {
197 0 : aosTO.SetNameValue("METHOD", "RPC");
198 : }
199 28 : else if (EQUAL(argv[i], "-geoloc"))
200 : {
201 0 : aosTO.SetNameValue("METHOD", "GEOLOC_ARRAY");
202 : }
203 28 : else if (EQUAL(argv[i], "-i"))
204 : {
205 1 : bInverse = TRUE;
206 : }
207 27 : else if (EQUAL(argv[i], "-to"))
208 : {
209 2 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
210 2 : aosTO.AddString(argv[++i]);
211 : }
212 25 : else if (EQUAL(argv[i], "-gcp"))
213 : {
214 12 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(4);
215 12 : char *endptr = nullptr;
216 : /* -gcp pixel line easting northing [elev] */
217 :
218 12 : nGCPCount++;
219 : pasGCPs = static_cast<GDAL_GCP *>(
220 12 : CPLRealloc(pasGCPs, sizeof(GDAL_GCP) * nGCPCount));
221 12 : GDALInitGCPs(1, pasGCPs + nGCPCount - 1);
222 :
223 : // coverity[tainted_data]
224 12 : pasGCPs[nGCPCount - 1].dfGCPPixel = CPLAtof(argv[++i]);
225 : // coverity[tainted_data]
226 12 : pasGCPs[nGCPCount - 1].dfGCPLine = CPLAtof(argv[++i]);
227 : // coverity[tainted_data]
228 12 : pasGCPs[nGCPCount - 1].dfGCPX = CPLAtof(argv[++i]);
229 : // coverity[tainted_data]
230 12 : pasGCPs[nGCPCount - 1].dfGCPY = CPLAtof(argv[++i]);
231 21 : if (argv[i + 1] != nullptr &&
232 9 : (CPLStrtod(argv[i + 1], &endptr) != 0.0 ||
233 9 : argv[i + 1][0] == '0'))
234 : {
235 : // Check that last argument is really a number and not a
236 : // filename looking like a number (see ticket #863).
237 3 : if (endptr && *endptr == 0)
238 : {
239 : // coverity[tainted_data]
240 3 : pasGCPs[nGCPCount - 1].dfGCPZ = CPLAtof(argv[++i]);
241 : }
242 : }
243 :
244 : /* should set id and info? */
245 : }
246 13 : else if (EQUAL(argv[i], "-output_xy"))
247 : {
248 2 : bOutputXY = TRUE;
249 : }
250 11 : else if (EQUAL(argv[i], "-ignore_extra_input"))
251 : {
252 1 : bIgnoreExtraInput = true;
253 : }
254 10 : else if (EQUAL(argv[i], "-E"))
255 : {
256 2 : bEchoInput = true;
257 : }
258 8 : else if (i < argc - 1 && EQUAL(argv[i], "-field_sep"))
259 : {
260 4 : osFieldSep = CPLString(argv[++i])
261 4 : .replaceAll("\\t", '\t')
262 4 : .replaceAll("\\r", '\r')
263 2 : .replaceAll("\\n", '\n');
264 : }
265 6 : else if (EQUAL(argv[i], "-coord") && i + 2 < argc)
266 : {
267 1 : bCoordOnCommandLine = true;
268 1 : dfX = CPLAtof(argv[++i]);
269 1 : dfY = CPLAtof(argv[++i]);
270 2 : if (i + 1 < argc &&
271 1 : CPLGetValueType(argv[i + 1]) != CPL_VALUE_STRING)
272 1 : dfZ = CPLAtof(argv[++i]);
273 2 : if (i + 1 < argc &&
274 1 : CPLGetValueType(argv[i + 1]) != CPL_VALUE_STRING)
275 1 : dfT = CPLAtof(argv[++i]);
276 : }
277 5 : else if (argv[i][0] == '-')
278 : {
279 0 : Usage(true, CPLSPrintf("Unknown option name '%s'", argv[i]));
280 : }
281 5 : else if (pszSrcFilename == nullptr)
282 : {
283 4 : pszSrcFilename = argv[i];
284 : }
285 1 : else if (pszDstFilename == nullptr)
286 : {
287 1 : pszDstFilename = argv[i];
288 : }
289 : else
290 : {
291 0 : Usage(true, "Too many command options.");
292 : }
293 : }
294 :
295 : /* -------------------------------------------------------------------- */
296 : /* Open src and destination file, if appropriate. */
297 : /* -------------------------------------------------------------------- */
298 14 : GDALDatasetH hSrcDS = nullptr;
299 14 : if (pszSrcFilename != nullptr)
300 : {
301 4 : hSrcDS = GDALOpen(pszSrcFilename, GA_ReadOnly);
302 4 : if (hSrcDS == nullptr)
303 0 : exit(1);
304 : }
305 :
306 14 : GDALDatasetH hDstDS = nullptr;
307 14 : if (pszDstFilename != nullptr)
308 : {
309 1 : hDstDS = GDALOpen(pszDstFilename, GA_ReadOnly);
310 1 : if (hDstDS == nullptr)
311 0 : exit(1);
312 : }
313 :
314 14 : if (hSrcDS != nullptr && nGCPCount > 0)
315 : {
316 0 : fprintf(stderr, "Command line GCPs and input file specified, "
317 : "specify one or the other.\n");
318 0 : exit(1);
319 : }
320 :
321 : /* -------------------------------------------------------------------- */
322 : /* Create a transformation object from the source to */
323 : /* destination coordinate system. */
324 : /* -------------------------------------------------------------------- */
325 14 : if (nGCPCount != 0 && nOrder == -1)
326 : {
327 1 : pfnTransformer = GDALTPSTransform;
328 1 : hTransformArg = GDALCreateTPSTransformer(nGCPCount, pasGCPs, FALSE);
329 : }
330 13 : else if (nGCPCount != 0)
331 : {
332 2 : pfnTransformer = GDALGCPTransform;
333 : hTransformArg =
334 2 : GDALCreateGCPTransformer(nGCPCount, pasGCPs, nOrder, FALSE);
335 : }
336 : else
337 : {
338 11 : pfnTransformer = GDALGenImgProjTransform;
339 : hTransformArg =
340 11 : GDALCreateGenImgProjTransformer2(hSrcDS, hDstDS, aosTO.List());
341 : }
342 :
343 14 : if (hTransformArg == nullptr)
344 : {
345 0 : exit(1);
346 : }
347 :
348 : /* -------------------------------------------------------------------- */
349 : /* Read points from stdin, transform and write to stdout. */
350 : /* -------------------------------------------------------------------- */
351 14 : double dfLastT = 0.0;
352 :
353 14 : if (!bCoordOnCommandLine)
354 : {
355 : // Is it an interactive terminal ?
356 13 : if (CPLIsInteractive(stdin))
357 : {
358 0 : if (pszSrcFilename != nullptr)
359 : {
360 0 : fprintf(stderr, "Enter column line values separated by space, "
361 : "and press Return.\n");
362 : }
363 : else
364 : {
365 0 : fprintf(stderr, "Enter X Y [Z [T]] values separated by space, "
366 : "and press Return.\n");
367 : }
368 : }
369 : }
370 :
371 14 : int nLine = 0;
372 39 : while (bCoordOnCommandLine || !feof(stdin))
373 : {
374 39 : std::string osExtraContent;
375 39 : if (!bCoordOnCommandLine)
376 : {
377 : char szLine[1024];
378 :
379 38 : if (fgets(szLine, sizeof(szLine) - 1, stdin) == nullptr)
380 13 : break;
381 :
382 25 : size_t nLen = strlen(szLine);
383 25 : if (nLen && szLine[nLen - 1] == '\n')
384 25 : szLine[--nLen] = 0;
385 25 : if (nLen && szLine[nLen - 1] == '\r')
386 0 : szLine[--nLen] = 0;
387 25 : const CPLStringList aosTokens(CSLTokenizeString(szLine));
388 25 : const int nCount = aosTokens.size();
389 :
390 25 : ++nLine;
391 25 : if (nCount < 2)
392 : {
393 0 : fprintf(stderr, "Not enough values at line %d\n", nLine);
394 0 : continue;
395 : }
396 :
397 25 : dfX = CPLAtof(aosTokens[0]);
398 25 : dfY = CPLAtof(aosTokens[1]);
399 25 : dfZ = 0.0;
400 25 : dfT = 0.0;
401 25 : int iStartExtraContent = nCount;
402 25 : if (nCount >= 3)
403 : {
404 10 : if (CPLGetValueType(aosTokens[2]) == CPL_VALUE_STRING)
405 : {
406 1 : iStartExtraContent = 2;
407 : }
408 : else
409 : {
410 9 : dfZ = CPLAtof(aosTokens[2]);
411 :
412 9 : if (nCount >= 4)
413 : {
414 5 : if (CPLGetValueType(aosTokens[3]) == CPL_VALUE_STRING)
415 : {
416 4 : iStartExtraContent = 3;
417 : }
418 : else
419 : {
420 1 : dfT = CPLAtof(aosTokens[3]);
421 1 : iStartExtraContent = 4;
422 : }
423 : }
424 : }
425 : }
426 :
427 25 : if (!bIgnoreExtraInput)
428 : {
429 37 : for (int i = iStartExtraContent; i < nCount; ++i)
430 : {
431 13 : if (!osExtraContent.empty())
432 8 : osExtraContent += ' ';
433 13 : osExtraContent += aosTokens[i];
434 : }
435 29 : while (!osExtraContent.empty() &&
436 5 : isspace(static_cast<int>(osExtraContent.back())))
437 : {
438 0 : osExtraContent.pop_back();
439 : }
440 24 : if (!osExtraContent.empty())
441 5 : osExtraContent = osFieldSep + osExtraContent;
442 : }
443 : }
444 26 : if (dfT != dfLastT && nGCPCount == 0)
445 : {
446 2 : if (dfT != 0.0)
447 : {
448 2 : aosTO.SetNameValue("COORDINATE_EPOCH", CPLSPrintf("%g", dfT));
449 : }
450 : else
451 : {
452 0 : aosTO.SetNameValue("COORDINATE_EPOCH", nullptr);
453 : }
454 2 : GDALDestroyGenImgProjTransformer(hTransformArg);
455 : hTransformArg =
456 2 : GDALCreateGenImgProjTransformer2(hSrcDS, hDstDS, aosTO.List());
457 : }
458 :
459 26 : int bSuccess = TRUE;
460 26 : const double dfXBefore = dfX;
461 26 : const double dfYBefore = dfY;
462 26 : const double dfZBefore = dfZ;
463 26 : if (pfnTransformer(hTransformArg, bInverse, 1, &dfX, &dfY, &dfZ,
464 52 : &bSuccess) &&
465 26 : bSuccess)
466 : {
467 26 : if (bEchoInput)
468 : {
469 2 : if (bOutputXY)
470 1 : CPLprintf("%.15g%s%.15g%s", dfXBefore, osFieldSep.c_str(),
471 : dfYBefore, osFieldSep.c_str());
472 : else
473 1 : CPLprintf("%.15g%s%.15g%s%.15g%s", dfXBefore,
474 : osFieldSep.c_str(), dfYBefore, osFieldSep.c_str(),
475 : dfZBefore, osFieldSep.c_str());
476 : }
477 26 : if (bOutputXY)
478 2 : CPLprintf("%.15g%s%.15g%s\n", dfX, osFieldSep.c_str(), dfY,
479 : osExtraContent.c_str());
480 : else
481 24 : CPLprintf("%.15g%s%.15g%s%.15g%s\n", dfX, osFieldSep.c_str(),
482 : dfY, osFieldSep.c_str(), dfZ, osExtraContent.c_str());
483 : }
484 : else
485 : {
486 0 : printf("transformation failed.\n");
487 : }
488 :
489 26 : if (bCoordOnCommandLine)
490 1 : break;
491 25 : dfLastT = dfT;
492 : }
493 :
494 14 : if (nGCPCount != 0 && nOrder == -1)
495 : {
496 1 : GDALDestroyTPSTransformer(hTransformArg);
497 : }
498 13 : else if (nGCPCount != 0)
499 : {
500 2 : GDALDestroyGCPTransformer(hTransformArg);
501 : }
502 : else
503 : {
504 11 : GDALDestroyGenImgProjTransformer(hTransformArg);
505 : }
506 :
507 14 : if (nGCPCount)
508 : {
509 3 : GDALDeinitGCPs(nGCPCount, pasGCPs);
510 3 : CPLFree(pasGCPs);
511 : }
512 :
513 14 : if (hSrcDS)
514 4 : GDALClose(hSrcDS);
515 :
516 14 : if (hDstDS)
517 1 : GDALClose(hDstDS);
518 :
519 14 : GDALDumpOpenDatasets(stderr);
520 14 : GDALDestroyDriverManager();
521 :
522 14 : CSLDestroy(argv);
523 :
524 14 : return 0;
525 : }
526 :
527 0 : MAIN_END
|