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