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