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