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
Usage(const char * pszErrorMsg=nullptr)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
IsValidSRS(const char * pszUserInput)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
MAIN_START(argc,argv)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