1 /******************************************************************************
2 *
3 * Project: GDAL Utilities
4 * Purpose: GDAL Image Translator Program
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 1998, 2002, Frank Warmerdam
9 * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys.com>
10 * Copyright (c) 2015, Faza Mahamood
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 ****************************************************************************/
30
31 #include "cpl_port.h"
32 #include "gdal_utils.h"
33 #include "gdal_utils_priv.h"
34
35 #include <cmath>
36 #include <cstdlib>
37 #include <cstring>
38
39 #include <algorithm>
40 #include <limits>
41
42 #include "commonutils.h"
43 #include "cpl_conv.h"
44 #include "cpl_error.h"
45 #include "cpl_json.h"
46 #include "cpl_progress.h"
47 #include "cpl_string.h"
48 #include "cpl_vsi.h"
49 #include "gdal.h"
50 #include "gdal_priv.h"
51 #include "gdal_priv_templates.hpp"
52 #include "gdal_rat.h"
53 #include "gdal_vrt.h"
54 #include "ogr_core.h"
55 #include "ogr_spatialref.h"
56 #include "vrtdataset.h"
57
58 CPL_CVSID("$Id: gdal_translate_lib.cpp e464272048d535fecb3378c0d33efd3e0f8286c8 2021-04-19 22:46:09 +0200 Even Rouault $")
59
60 static int ArgIsNumeric( const char * );
61 static void AttachMetadata( GDALDatasetH, char ** );
62 static void CopyBandInfo( GDALRasterBand * poSrcBand, GDALRasterBand * poDstBand,
63 int bCanCopyStatsMetadata, int bCopyScale, int bCopyNoData, bool bCopyRAT,
64 const GDALTranslateOptions* psOptions );
65
66 typedef enum
67 {
68 MASK_DISABLED,
69 MASK_AUTO,
70 MASK_USER
71 } MaskMode;
72
73 /************************************************************************/
74 /* GDALTranslateScaleParams */
75 /************************************************************************/
76
77 /** scaling parameters for use in GDALTranslateOptions.
78 */
79 typedef struct
80 {
81 /*! scaling is done only if it is set to TRUE. This is helpful when there is a need to
82 scale only certain bands. */
83 int bScale;
84
85 /*! set it to TRUE if dfScaleSrcMin and dfScaleSrcMax is set. When it is FALSE, the
86 input range is automatically computed from the source data. */
87 bool bHaveScaleSrc;
88
89 /*! the range of input pixel values which need to be scaled */
90 double dfScaleSrcMin;
91 double dfScaleSrcMax;
92
93 /*! the range of output pixel values. If GDALTranslateScaleParams::dfScaleDstMin
94 and GDALTranslateScaleParams::dfScaleDstMax are not set, then the output
95 range is 0 to 255. */
96 double dfScaleDstMin;
97 double dfScaleDstMax;
98 } GDALTranslateScaleParams;
99
100 /************************************************************************/
101 /* GDALTranslateOptions */
102 /************************************************************************/
103
104 /** Options for use with GDALTranslate(). GDALTranslateOptions* must be allocated
105 * and freed with GDALTranslateOptionsNew() and GDALTranslateOptionsFree() respectively.
106 */
107 struct GDALTranslateOptions
108 {
109
110 /*! output format. Use the short format name. */
111 char *pszFormat;
112
113 /*! allow or suppress progress monitor and other non-error output */
114 bool bQuiet;
115
116 /*! the progress function to use */
117 GDALProgressFunc pfnProgress;
118
119 /*! pointer to the progress data variable */
120 void *pProgressData;
121
122 /*! for the output bands to be of the indicated data type */
123 GDALDataType eOutputType;
124
125 MaskMode eMaskMode;
126
127 /*! number of input bands to write to the output file, or to reorder bands */
128 int nBandCount;
129
130 /*! list of input bands to write to the output file, or to reorder bands. The
131 value 1 corresponds to the 1st band. */
132 int *panBandList; /* negative value of panBandList[i] means mask band of ABS(panBandList[i]) */
133
134 /*! size of the output file. GDALTranslateOptions::nOXSizePixel is in pixels and
135 GDALTranslateOptions::nOYSizePixel is in lines. If one of the two values is
136 set to 0, its value will be determined from the other one, while maintaining
137 the aspect ratio of the source dataset */
138 int nOXSizePixel;
139 int nOYSizePixel;
140
141 /*! size of the output file. GDALTranslateOptions::dfOXSizePct and GDALTranslateOptions::dfOYSizePct
142 are fraction of the input image size. The value 100 means 100%. If one of the two values is set
143 to 0, its value will be determined from the other one, while maintaining the aspect ratio of the
144 source dataset */
145 double dfOXSizePct;
146 double dfOYSizePct;
147
148 /*! list of creation options to the output format driver */
149 char **papszCreateOptions;
150
151 /*! subwindow from the source image for copying based on pixel/line location */
152 double adfSrcWin[4];
153
154 /*! don't be forgiving of mismatches and lost data when translating to the output format */
155 bool bStrict;
156
157 /*! apply the scale/offset metadata for the bands to convert scaled values to unscaled values.
158 * It is also often necessary to reset the output datatype with GDALTranslateOptions::eOutputType */
159 bool bUnscale;
160
161 bool bSetScale;
162
163 double dfScale;
164
165 bool bSetOffset;
166
167 double dfOffset;
168
169 /*! the size of pasScaleParams */
170 int nScaleRepeat;
171
172 /*! the list of scale parameters for each band. */
173 GDALTranslateScaleParams *pasScaleParams;
174
175 /*! It is set to TRUE, when scale parameters are specific to each band */
176 bool bHasUsedExplicitScaleBand;
177
178 /*! the size of the list padfExponent */
179 int nExponentRepeat;
180
181 /*! to apply non-linear scaling with a power function. It is the list of exponents of the power
182 function (must be positive). This option must be used with GDALTranslateOptions::pasScaleParams. If
183 GDALTranslateOptions::nExponentRepeat is 1, it is applied to all bands of the output image. */
184 double *padfExponent;
185
186 bool bHasUsedExplicitExponentBand;
187
188 /*! list of metadata key and value to set on the output dataset if possible.
189 * GDALTranslateOptionsSetMetadataOptions() and GDALTranslateOptionsAddMetadataOptions()
190 * should be used */
191 char **papszMetadataOptions;
192
193 /*! override the projection for the output file. The SRS may be any of the usual
194 GDAL/OGR forms, complete WKT, PROJ.4, EPSG:n or a file containing the WKT. */
195 char *pszOutputSRS;
196
197 /*! does not copy source GCP into destination dataset (when TRUE) */
198 bool bNoGCP;
199
200 /*! number of GCPS to be added to the output dataset */
201 int nGCPCount;
202
203 /*! list of GCPs to be added to the output dataset */
204 GDAL_GCP *pasGCPs;
205
206 /*! assign/override the georeferenced bounds of the output file. This assigns
207 georeferenced bounds to the output file, ignoring what would have been
208 derived from the source file. So this does not cause reprojection to the
209 specified SRS. */
210 double adfULLR[4];
211
212 /*! set a nodata value specified in GDALTranslateOptions::dfNoDataReal to the output bands */
213 bool bSetNoData;
214
215 /*! avoid setting a nodata value to the output file if one exists for the source file */
216 bool bUnsetNoData;
217
218 /*! Assign a specified nodata value to output bands ( GDALTranslateOptions::bSetNoData option
219 should be set). Note that if the input dataset has a nodata value, this does not cause
220 pixel values that are equal to that nodata value to be changed to the value specified. */
221 double dfNoDataReal;
222
223 /*! to expose a dataset with 1 band with a color table as a dataset with
224 3 (RGB) or 4 (RGBA) bands. Useful for output drivers such as JPEG,
225 JPEG2000, MrSID, ECW that don't support color indexed datasets.
226 The 1 value enables to expand a dataset with a color table that only
227 contains gray levels to a gray indexed dataset. */
228 int nRGBExpand;
229
230 int nMaskBand; /* negative value means mask band of ABS(nMaskBand) */
231
232 /*! force recomputation of statistics */
233 bool bStats;
234
235 bool bApproxStats;
236
237 /*! If this option is set, GDALTranslateOptions::adfSrcWin or (GDALTranslateOptions::dfULX,
238 GDALTranslateOptions::dfULY, GDALTranslateOptions::dfLRX, GDALTranslateOptions::dfLRY)
239 values that falls partially outside the source raster extent will be considered
240 as an error. The default behavior is to accept such requests. */
241 bool bErrorOnPartiallyOutside;
242
243 /*! Same as bErrorOnPartiallyOutside, except that the criterion for
244 erroring out is when the request falls completely outside the
245 source raster extent. */
246 bool bErrorOnCompletelyOutside;
247
248 /*! does not copy source RAT into destination dataset (when TRUE) */
249 bool bNoRAT;
250
251 /*! resampling algorithm
252 nearest (default), bilinear, cubic, cubicspline, lanczos, average, mode */
253 char *pszResampling;
254
255 /*! target resolution. The values must be expressed in georeferenced units.
256 Both must be positive values. This is exclusive with GDALTranslateOptions::nOXSizePixel
257 (or GDALTranslateOptions::dfOXSizePct), GDALTranslateOptions::nOYSizePixel
258 (or GDALTranslateOptions::dfOYSizePct) and GDALTranslateOptions::adfULLR */
259 double dfXRes;
260 double dfYRes;
261
262 /*! subwindow from the source image for copying (like GDALTranslateOptions::adfSrcWin)
263 but with the corners given in georeferenced coordinates (by default
264 expressed in the SRS of the dataset. Can be changed with
265 pszProjSRS) */
266 double dfULX;
267 double dfULY;
268 double dfLRX;
269 double dfLRY;
270
271 /*! SRS in which to interpret the coordinates given with GDALTranslateOptions::dfULX,
272 GDALTranslateOptions::dfULY, GDALTranslateOptions::dfLRX, GDALTranslateOptions::dfLRY.
273 The SRS may be any of the usual GDAL/OGR forms, complete WKT, PROJ.4, EPSG:n or
274 a file containing the WKT. Note that this does not cause reprojection of the
275 dataset to the specified SRS. */
276 char *pszProjSRS;
277
278 int nLimitOutSize;
279
280 // Array of color interpretations per band. Should be a GDALColorInterp
281 // value, or -1 if no override.
282 int nColorInterpSize;
283 int* panColorInterp;
284
285 /*! does not copy source XMP into destination dataset (when TRUE) */
286 bool bNoXMP;
287 };
288
289 /************************************************************************/
290 /* SrcToDst() */
291 /************************************************************************/
292
SrcToDst(double dfX,double dfY,double dfSrcXOff,double dfSrcYOff,double dfSrcXSize,double dfSrcYSize,double dfDstXOff,double dfDstYOff,double dfDstXSize,double dfDstYSize,double & dfXOut,double & dfYOut)293 static void SrcToDst( double dfX, double dfY,
294 double dfSrcXOff, double dfSrcYOff,
295 double dfSrcXSize, double dfSrcYSize,
296 double dfDstXOff, double dfDstYOff,
297 double dfDstXSize, double dfDstYSize,
298 double &dfXOut, double &dfYOut )
299
300 {
301 dfXOut = ((dfX - dfSrcXOff) / dfSrcXSize) * dfDstXSize + dfDstXOff;
302 dfYOut = ((dfY - dfSrcYOff) / dfSrcYSize) * dfDstYSize + dfDstYOff;
303 }
304
305 /************************************************************************/
306 /* GetSrcDstWindow() */
307 /************************************************************************/
308
FixSrcDstWindow(double * padfSrcWin,double * padfDstWin,int nSrcRasterXSize,int nSrcRasterYSize)309 static bool FixSrcDstWindow( double* padfSrcWin, double* padfDstWin,
310 int nSrcRasterXSize,
311 int nSrcRasterYSize )
312
313 {
314 const double dfSrcXOff = padfSrcWin[0];
315 const double dfSrcYOff = padfSrcWin[1];
316 const double dfSrcXSize = padfSrcWin[2];
317 const double dfSrcYSize = padfSrcWin[3];
318
319 const double dfDstXOff = padfDstWin[0];
320 const double dfDstYOff = padfDstWin[1];
321 const double dfDstXSize = padfDstWin[2];
322 const double dfDstYSize = padfDstWin[3];
323
324 bool bModifiedX = false;
325 bool bModifiedY = false;
326
327 double dfModifiedSrcXOff = dfSrcXOff;
328 double dfModifiedSrcYOff = dfSrcYOff;
329
330 double dfModifiedSrcXSize = dfSrcXSize;
331 double dfModifiedSrcYSize = dfSrcYSize;
332
333 /* -------------------------------------------------------------------- */
334 /* Clamp within the bounds of the available source data. */
335 /* -------------------------------------------------------------------- */
336 if( dfModifiedSrcXOff < 0 )
337 {
338 dfModifiedSrcXSize += dfModifiedSrcXOff;
339 dfModifiedSrcXOff = 0;
340
341 bModifiedX = true;
342 }
343
344 if( dfModifiedSrcYOff < 0 )
345 {
346 dfModifiedSrcYSize += dfModifiedSrcYOff;
347 dfModifiedSrcYOff = 0;
348 bModifiedY = true;
349 }
350
351 if( dfModifiedSrcXOff + dfModifiedSrcXSize > nSrcRasterXSize )
352 {
353 dfModifiedSrcXSize = nSrcRasterXSize - dfModifiedSrcXOff;
354 bModifiedX = true;
355 }
356
357 if( dfModifiedSrcYOff + dfModifiedSrcYSize > nSrcRasterYSize )
358 {
359 dfModifiedSrcYSize = nSrcRasterYSize - dfModifiedSrcYOff;
360 bModifiedY = true;
361 }
362
363 /* -------------------------------------------------------------------- */
364 /* Don't do anything if the requesting region is completely off */
365 /* the source image. */
366 /* -------------------------------------------------------------------- */
367 if( dfModifiedSrcXOff >= nSrcRasterXSize
368 || dfModifiedSrcYOff >= nSrcRasterYSize
369 || dfModifiedSrcXSize <= 0 || dfModifiedSrcYSize <= 0 )
370 {
371 return false;
372 }
373
374 padfSrcWin[0] = dfModifiedSrcXOff;
375 padfSrcWin[1] = dfModifiedSrcYOff;
376 padfSrcWin[2] = dfModifiedSrcXSize;
377 padfSrcWin[3] = dfModifiedSrcYSize;
378
379 /* -------------------------------------------------------------------- */
380 /* If we haven't had to modify the source rectangle, then the */
381 /* destination rectangle must be the whole region. */
382 /* -------------------------------------------------------------------- */
383 if( !bModifiedX && !bModifiedY )
384 return true;
385
386 /* -------------------------------------------------------------------- */
387 /* Now transform this possibly reduced request back into the */
388 /* destination buffer coordinates in case the output region is */
389 /* less than the whole buffer. */
390 /* -------------------------------------------------------------------- */
391 double dfDstULX, dfDstULY, dfDstLRX, dfDstLRY;
392
393 SrcToDst( dfModifiedSrcXOff, dfModifiedSrcYOff,
394 dfSrcXOff, dfSrcYOff,
395 dfSrcXSize, dfSrcYSize,
396 dfDstXOff, dfDstYOff,
397 dfDstXSize, dfDstYSize,
398 dfDstULX, dfDstULY );
399 SrcToDst( dfModifiedSrcXOff + dfModifiedSrcXSize, dfModifiedSrcYOff + dfModifiedSrcYSize,
400 dfSrcXOff, dfSrcYOff,
401 dfSrcXSize, dfSrcYSize,
402 dfDstXOff, dfDstYOff,
403 dfDstXSize, dfDstYSize,
404 dfDstLRX, dfDstLRY );
405
406 double dfModifiedDstXOff = dfDstXOff;
407 double dfModifiedDstYOff = dfDstYOff;
408 double dfModifiedDstXSize = dfDstXSize;
409 double dfModifiedDstYSize = dfDstYSize;
410
411 if( bModifiedX )
412 {
413 dfModifiedDstXOff = dfDstULX - dfDstXOff;
414 dfModifiedDstXSize = (dfDstLRX - dfDstXOff) - dfModifiedDstXOff;
415
416 dfModifiedDstXOff = std::max(0.0, dfModifiedDstXOff);
417 if( dfModifiedDstXOff + dfModifiedDstXSize > dfDstXSize )
418 dfModifiedDstXSize = dfDstXSize - dfModifiedDstXOff;
419 }
420
421 if( bModifiedY )
422 {
423 dfModifiedDstYOff = dfDstULY - dfDstYOff;
424 dfModifiedDstYSize = (dfDstLRY - dfDstYOff) - dfModifiedDstYOff;
425
426 dfModifiedDstYOff = std::max(0.0, dfModifiedDstYOff);
427 if( dfModifiedDstYOff + dfModifiedDstYSize > dfDstYSize )
428 dfModifiedDstYSize = dfDstYSize - dfModifiedDstYOff;
429 }
430
431 if( dfModifiedDstXSize <= 0.0 || dfModifiedDstYSize <= 0.0 )
432 {
433 return false;
434 }
435
436 padfDstWin[0] = dfModifiedDstXOff;
437 padfDstWin[1] = dfModifiedDstYOff;
438 padfDstWin[2] = dfModifiedDstXSize;
439 padfDstWin[3] = dfModifiedDstYSize;
440
441 return true;
442 }
443
444 /************************************************************************/
445 /* GDALTranslateOptionsClone() */
446 /************************************************************************/
447
448 static
GDALTranslateOptionsClone(const GDALTranslateOptions * psOptionsIn)449 GDALTranslateOptions* GDALTranslateOptionsClone(const GDALTranslateOptions *psOptionsIn)
450 {
451 GDALTranslateOptions* psOptions = static_cast<GDALTranslateOptions*>(
452 CPLMalloc(sizeof(GDALTranslateOptions)));
453 memcpy(psOptions, psOptionsIn, sizeof(GDALTranslateOptions));
454 if( psOptionsIn->pszFormat ) psOptions->pszFormat = CPLStrdup(psOptionsIn->pszFormat);
455 if( psOptionsIn->panBandList )
456 {
457 psOptions->panBandList =
458 static_cast<int *>(CPLMalloc(sizeof(int) * psOptions->nBandCount));
459 memcpy(psOptions->panBandList, psOptionsIn->panBandList,
460 sizeof(int) * psOptions->nBandCount);
461 }
462 psOptions->papszCreateOptions = CSLDuplicate(psOptionsIn->papszCreateOptions);
463 if( psOptionsIn->pasScaleParams )
464 {
465 psOptions->pasScaleParams = static_cast<GDALTranslateScaleParams *>(
466 CPLMalloc(sizeof(GDALTranslateScaleParams) *
467 psOptions->nScaleRepeat));
468 memcpy(psOptions->pasScaleParams, psOptionsIn->pasScaleParams,
469 sizeof(GDALTranslateScaleParams) * psOptions->nScaleRepeat);
470 }
471 if( psOptionsIn->padfExponent )
472 {
473 psOptions->padfExponent = static_cast<double *>(
474 CPLMalloc(sizeof(double) * psOptions->nExponentRepeat));
475 memcpy(psOptions->padfExponent, psOptionsIn->padfExponent,
476 sizeof(double) * psOptions->nExponentRepeat);
477 }
478 psOptions->papszMetadataOptions = CSLDuplicate(psOptionsIn->papszMetadataOptions);
479 if( psOptionsIn->pszOutputSRS ) psOptions->pszOutputSRS = CPLStrdup(psOptionsIn->pszOutputSRS);
480 if( psOptionsIn->nGCPCount )
481 psOptions->pasGCPs = GDALDuplicateGCPs( psOptionsIn->nGCPCount, psOptionsIn->pasGCPs );
482 if( psOptionsIn->pszResampling ) psOptions->pszResampling = CPLStrdup(psOptionsIn->pszResampling);
483 if( psOptionsIn->pszProjSRS ) psOptions->pszProjSRS = CPLStrdup(psOptionsIn->pszProjSRS);
484 if( psOptionsIn->panColorInterp )
485 {
486 psOptions->panColorInterp =
487 static_cast<int *>(CPLMalloc(sizeof(int) * psOptions->nColorInterpSize));
488 memcpy(psOptions->panColorInterp, psOptionsIn->panColorInterp,
489 sizeof(int) * psOptions->nColorInterpSize);
490 }
491 return psOptions;
492 }
493
494 /************************************************************************/
495 /* GDALTranslateFlush() */
496 /************************************************************************/
497
GDALTranslateFlush(GDALDatasetH hOutDS)498 static GDALDatasetH GDALTranslateFlush(GDALDatasetH hOutDS)
499 {
500 if( hOutDS != nullptr )
501 {
502 CPLErr eErrBefore = CPLGetLastErrorType();
503 GDALFlushCache( hOutDS );
504 if (eErrBefore == CE_None &&
505 CPLGetLastErrorType() != CE_None)
506 {
507 GDALClose(hOutDS);
508 hOutDS = nullptr;
509 }
510 }
511 return hOutDS;
512 }
513
514 /************************************************************************/
515 /* EditISIS3MetadataForBandChange() */
516 /************************************************************************/
517
Clone(const CPLJSONObject & obj)518 static CPLJSONObject Clone(const CPLJSONObject& obj)
519 {
520 auto serialized = obj.Format(CPLJSONObject::PrettyFormat::Plain);
521 CPLJSONDocument oJSONDocument;
522 const GByte *pabyData = reinterpret_cast<const GByte *>(serialized.c_str());
523 oJSONDocument.LoadMemory( pabyData );
524 return oJSONDocument.GetRoot();
525 }
526
ReworkArray(CPLJSONObject & container,const CPLJSONObject & obj,int nSrcBandCount,const GDALTranslateOptions * psOptions)527 static void ReworkArray(CPLJSONObject& container, const CPLJSONObject& obj,
528 int nSrcBandCount,
529 const GDALTranslateOptions *psOptions)
530 {
531 auto oArray = obj.ToArray();
532 if( oArray.Size() == nSrcBandCount )
533 {
534 CPLJSONArray oNewArray;
535 for( int i = 0; i < psOptions->nBandCount; i++ )
536 {
537 const int iSrcIdx = psOptions->panBandList[i]-1;
538 oNewArray.Add(oArray[iSrcIdx]);
539 }
540 const auto childName(obj.GetName());
541 container.Delete(childName);
542 container.Add(childName, oNewArray);
543 }
544 }
545
EditISIS3MetadataForBandChange(const char * pszJSON,int nSrcBandCount,const GDALTranslateOptions * psOptions)546 static CPLString EditISIS3MetadataForBandChange(const char* pszJSON,
547 int nSrcBandCount,
548 const GDALTranslateOptions *psOptions)
549 {
550 CPLJSONDocument oJSONDocument;
551 const GByte *pabyData = reinterpret_cast<const GByte *>(pszJSON);
552 if( !oJSONDocument.LoadMemory( pabyData ) )
553 {
554 return CPLString();
555 }
556
557 auto oRoot = oJSONDocument.GetRoot();
558 if( !oRoot.IsValid() )
559 {
560 return CPLString();
561 }
562
563 auto oBandBin = oRoot.GetObj( "IsisCube/BandBin" );
564 if( oBandBin.IsValid() && oBandBin.GetType() == CPLJSONObject::Type::Object )
565 {
566 // Backup original BandBin object
567 oRoot.GetObj("IsisCube").Add("OriginalBandBin", Clone(oBandBin));
568
569 // Iterate over BandBin members and reorder/resize its arrays that
570 // have the same number of elements than the number of bands of the
571 // source dataset.
572 for( auto& child: oBandBin.GetChildren() )
573 {
574 if( child.GetType() == CPLJSONObject::Type::Array )
575 {
576 ReworkArray(oBandBin, child, nSrcBandCount, psOptions);
577 }
578 else if( child.GetType() == CPLJSONObject::Type::Object )
579 {
580 auto oValue = child.GetObj("value");
581 auto oUnit = child.GetObj("unit");
582 if( oValue.GetType() == CPLJSONObject::Type::Array )
583 {
584 ReworkArray(child, oValue, nSrcBandCount, psOptions);
585 }
586 }
587 }
588 }
589
590 return oRoot.Format(CPLJSONObject::PrettyFormat::Pretty);
591 }
592
593 /************************************************************************/
594 /* AdjustNoDataValue() */
595 /************************************************************************/
596
AdjustNoDataValue(double dfInputNoDataValue,GDALRasterBand * poBand,const GDALTranslateOptions * psOptions)597 static double AdjustNoDataValue( double dfInputNoDataValue,
598 GDALRasterBand* poBand,
599 const GDALTranslateOptions *psOptions )
600 {
601 bool bSignedByte = false;
602 const char* pszPixelType = CSLFetchNameValue( psOptions->papszCreateOptions, "PIXELTYPE" );
603 if( pszPixelType == nullptr )
604 {
605 pszPixelType = poBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
606 }
607 if( pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE") )
608 bSignedByte = true;
609 int bClamped = FALSE;
610 int bRounded = FALSE;
611 double dfVal = 0.0;
612 const GDALDataType eBandType = poBand->GetRasterDataType();
613 if( bSignedByte )
614 {
615 if( dfInputNoDataValue < -128.0 )
616 {
617 dfVal = -128.0;
618 bClamped = TRUE;
619 }
620 else if( dfInputNoDataValue > 127.0 )
621 {
622 dfVal = 127.0;
623 bClamped = TRUE;
624 }
625 else
626 {
627 dfVal = static_cast<int>(floor(dfInputNoDataValue + 0.5));
628 if( dfVal != dfInputNoDataValue )
629 bRounded = TRUE;
630 }
631 }
632 else
633 {
634 dfVal = GDALAdjustValueToDataType(eBandType,
635 dfInputNoDataValue,
636 &bClamped, &bRounded );
637 }
638
639 if (bClamped)
640 {
641 CPLError( CE_Warning, CPLE_AppDefined, "for band %d, nodata value has been clamped "
642 "to %.0f, the original value being out of range.",
643 poBand->GetBand(), dfVal);
644 }
645 else if(bRounded)
646 {
647 CPLError( CE_Warning, CPLE_AppDefined, "for band %d, nodata value has been rounded "
648 "to %.0f, %s being an integer datatype.",
649 poBand->GetBand(), dfVal,
650 GDALGetDataTypeName(eBandType));
651 }
652 return dfVal;
653 }
654
655 /************************************************************************/
656 /* GDALTranslate() */
657 /************************************************************************/
658
659 /**
660 * Converts raster data between different formats.
661 *
662 * This is the equivalent of the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
663 *
664 * GDALTranslateOptions* must be allocated and freed with GDALTranslateOptionsNew()
665 * and GDALTranslateOptionsFree() respectively.
666 *
667 * @param pszDest the destination dataset path.
668 * @param hSrcDataset the source dataset handle.
669 * @param psOptionsIn the options struct returned by GDALTranslateOptionsNew() or NULL.
670 * @param pbUsageError pointer to a integer output variable to store if any usage error has occurred or NULL.
671 * @return the output dataset (new dataset that must be closed using GDALClose()) or NULL in case of error.
672 *
673 * @since GDAL 2.1
674 */
675
GDALTranslate(const char * pszDest,GDALDatasetH hSrcDataset,const GDALTranslateOptions * psOptionsIn,int * pbUsageError)676 GDALDatasetH GDALTranslate( const char *pszDest, GDALDatasetH hSrcDataset,
677 const GDALTranslateOptions *psOptionsIn, int *pbUsageError )
678
679 {
680 CPLErrorReset();
681 if( hSrcDataset == nullptr )
682 {
683 CPLError( CE_Failure, CPLE_AppDefined, "No source dataset specified.");
684
685 if(pbUsageError)
686 *pbUsageError = TRUE;
687 return nullptr;
688 }
689 if( pszDest == nullptr )
690 {
691 CPLError( CE_Failure, CPLE_AppDefined, "No target dataset specified.");
692
693 if(pbUsageError)
694 *pbUsageError = TRUE;
695 return nullptr;
696 }
697
698 GDALTranslateOptions* psOptions =
699 (psOptionsIn) ? GDALTranslateOptionsClone(psOptionsIn) :
700 GDALTranslateOptionsNew(nullptr, nullptr);
701
702 GDALDatasetH hOutDS = nullptr;
703 bool bGotBounds = false;
704
705 if(pbUsageError)
706 *pbUsageError = FALSE;
707
708 if(psOptions->adfULLR[0] != 0.0 || psOptions->adfULLR[1] != 0.0 || psOptions->adfULLR[2] != 0.0 || psOptions->adfULLR[3] != 0.0)
709 bGotBounds = true;
710
711 const char *pszSource = GDALGetDescription(hSrcDataset);
712
713 if( strcmp(pszSource, pszDest) == 0 && pszSource[0] != '\0' &&
714 GDALGetDatasetDriver(hSrcDataset) != GDALGetDriverByName("MEM") )
715 {
716 CPLError( CE_Failure, CPLE_AppDefined, "Source and destination datasets must be different.");
717
718 if(pbUsageError)
719 *pbUsageError = TRUE;
720 GDALTranslateOptionsFree(psOptions);
721 return nullptr;
722 }
723
724 CPLString osProjSRS;
725
726 if(psOptions->pszProjSRS != nullptr)
727 {
728 OGRSpatialReference oSRS;
729 oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
730
731 if( oSRS.SetFromUserInput( psOptions->pszProjSRS ) != OGRERR_NONE )
732 {
733 CPLError( CE_Failure, CPLE_AppDefined, "Failed to process SRS definition: %s",
734 psOptions->pszProjSRS );
735 GDALTranslateOptionsFree(psOptions);
736 return nullptr;
737 }
738
739 char* pszSRS = nullptr;
740 oSRS.exportToWkt( &pszSRS );
741 if( pszSRS )
742 osProjSRS = pszSRS;
743 CPLFree( pszSRS );
744 }
745
746 if(psOptions->pszOutputSRS != nullptr)
747 {
748 OGRSpatialReference oOutputSRS;
749 oOutputSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
750
751 if( oOutputSRS.SetFromUserInput( psOptions->pszOutputSRS ) != OGRERR_NONE )
752 {
753 CPLError( CE_Failure, CPLE_AppDefined, "Failed to process SRS definition: %s",
754 psOptions->pszOutputSRS );
755 GDALTranslateOptionsFree(psOptions);
756 return nullptr;
757 }
758
759 char* pszSRS = nullptr;
760 {
761 CPLErrorStateBackuper oErrorStateBackuper;
762 CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
763 if( oOutputSRS.exportToWkt( &pszSRS ) != OGRERR_NONE )
764 {
765 CPLFree(pszSRS);
766 pszSRS = nullptr;
767 const char* const apszOptions[] = { "FORMAT=WKT2", nullptr };
768 oOutputSRS.exportToWkt( &pszSRS, apszOptions );
769 }
770 }
771 CPLFree( psOptions->pszOutputSRS );
772 psOptions->pszOutputSRS = CPLStrdup( pszSRS );
773 CPLFree( pszSRS );
774 }
775
776 /* -------------------------------------------------------------------- */
777 /* Check that incompatible options are not used */
778 /* -------------------------------------------------------------------- */
779
780 if( (psOptions->nOXSizePixel != 0 || psOptions->dfOXSizePct != 0.0 || psOptions->nOYSizePixel != 0 ||
781 psOptions->dfOYSizePct != 0.0) && (psOptions->dfXRes != 0 && psOptions->dfYRes != 0) )
782 {
783 CPLError( CE_Failure, CPLE_IllegalArg, "-outsize and -tr options cannot be used at the same time.");
784 if(pbUsageError)
785 *pbUsageError = TRUE;
786 GDALTranslateOptionsFree(psOptions);
787 return nullptr;
788 }
789 if( bGotBounds && (psOptions->dfXRes != 0 && psOptions->dfYRes != 0) )
790 {
791 CPLError( CE_Failure, CPLE_IllegalArg, "-a_ullr and -tr options cannot be used at the same time.");
792 if(pbUsageError)
793 *pbUsageError = TRUE;
794 GDALTranslateOptionsFree(psOptions);
795 return nullptr;
796 }
797
798 /* -------------------------------------------------------------------- */
799 /* Collect some information from the source file. */
800 /* -------------------------------------------------------------------- */
801 const int nRasterXSize = GDALGetRasterXSize( hSrcDataset );
802 const int nRasterYSize = GDALGetRasterYSize( hSrcDataset );
803
804 if( psOptions->adfSrcWin[2] == 0 && psOptions->adfSrcWin[3] == 0 )
805 {
806 psOptions->adfSrcWin[2] = nRasterXSize;
807 psOptions->adfSrcWin[3] = nRasterYSize;
808 }
809
810 /* -------------------------------------------------------------------- */
811 /* Build band list to translate */
812 /* -------------------------------------------------------------------- */
813 bool bAllBandsInOrder = true;
814
815 if( psOptions->panBandList == nullptr )
816 {
817
818 psOptions->nBandCount = GDALGetRasterCount( hSrcDataset );
819 if( ( psOptions->nBandCount == 0 ) && (psOptions->bStrict ) )
820 {
821 // if not strict then the driver can fail if it doesn't support zero bands
822 CPLError( CE_Failure, CPLE_AppDefined, "Input file has no bands, and so cannot be translated." );
823 GDALTranslateOptionsFree(psOptions);
824 return nullptr;
825 }
826
827 psOptions->panBandList = static_cast<int *>(
828 CPLMalloc(sizeof(int) * psOptions->nBandCount));
829 for( int i = 0; i < psOptions->nBandCount; i++ )
830 psOptions->panBandList[i] = i+1;
831 }
832 else
833 {
834 for( int i = 0; i < psOptions->nBandCount; i++ )
835 {
836 if( std::abs(psOptions->panBandList[i]) >
837 GDALGetRasterCount(hSrcDataset) )
838 {
839 CPLError(CE_Failure, CPLE_AppDefined,
840 "Band %d requested, but only bands 1 to %d available.",
841 std::abs(psOptions->panBandList[i]),
842 GDALGetRasterCount(hSrcDataset) );
843 GDALTranslateOptionsFree(psOptions);
844 return nullptr;
845 }
846
847 if( psOptions->panBandList[i] != i+1 )
848 bAllBandsInOrder = FALSE;
849 }
850
851 if( psOptions->nBandCount != GDALGetRasterCount( hSrcDataset ) )
852 bAllBandsInOrder = FALSE;
853 }
854
855 if( psOptions->nScaleRepeat > psOptions->nBandCount )
856 {
857 if( !psOptions->bHasUsedExplicitScaleBand )
858 CPLError( CE_Failure, CPLE_IllegalArg, "-scale has been specified more times than the number of output bands");
859 else
860 CPLError( CE_Failure, CPLE_IllegalArg, "-scale_XX has been specified with XX greater than the number of output bands");
861 if(pbUsageError)
862 *pbUsageError = TRUE;
863 GDALTranslateOptionsFree(psOptions);
864 return nullptr;
865 }
866
867 if( psOptions->nExponentRepeat > psOptions->nBandCount )
868 {
869 if( !psOptions->bHasUsedExplicitExponentBand )
870 CPLError( CE_Failure, CPLE_IllegalArg, "-exponent has been specified more times than the number of output bands");
871 else
872 CPLError( CE_Failure, CPLE_IllegalArg, "-exponent_XX has been specified with XX greater than the number of output bands");
873 if(pbUsageError)
874 *pbUsageError = TRUE;
875 GDALTranslateOptionsFree(psOptions);
876 return nullptr;
877 }
878 /* -------------------------------------------------------------------- */
879 /* Compute the source window from the projected source window */
880 /* if the projected coordinates were provided. Note that the */
881 /* projected coordinates are in ulx, uly, lrx, lry format, */
882 /* while the adfSrcWin is xoff, yoff, xsize, ysize with the */
883 /* xoff,yoff being the ulx, uly in pixel/line. */
884 /* -------------------------------------------------------------------- */
885 const char *pszProjection = nullptr;
886
887 if( psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0
888 || psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0 )
889 {
890 double adfGeoTransform[6];
891
892 GDALGetGeoTransform( hSrcDataset, adfGeoTransform );
893
894 if( adfGeoTransform[1] == 0.0 || adfGeoTransform[5] == 0.0 )
895 {
896 CPLError( CE_Failure, CPLE_AppDefined,
897 "The -projwin option was used, but the geotransform is "
898 "invalid." );
899 GDALTranslateOptionsFree(psOptions);
900 return nullptr;
901 }
902 if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 )
903 {
904 CPLError( CE_Failure, CPLE_AppDefined,
905 "The -projwin option was used, but the geotransform is\n"
906 "rotated. This configuration is not supported." );
907 GDALTranslateOptionsFree(psOptions);
908 return nullptr;
909 }
910
911 if( !osProjSRS.empty() )
912 {
913 pszProjection = GDALGetProjectionRef( hSrcDataset );
914 if( pszProjection != nullptr && strlen(pszProjection) > 0 )
915 {
916 OGRSpatialReference oSRSIn;
917 OGRSpatialReference oSRSDS;
918 oSRSIn.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
919 oSRSDS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
920 oSRSIn.SetFromUserInput(osProjSRS);
921 oSRSDS.SetFromUserInput(pszProjection);
922 if( !oSRSIn.IsSame(&oSRSDS) )
923 {
924 OGRCoordinateTransformation* poCT = OGRCreateCoordinateTransformation(&oSRSIn, &oSRSDS);
925 if( !(poCT &&
926 poCT->Transform(1, &psOptions->dfULX, &psOptions->dfULY) &&
927 poCT->Transform(1, &psOptions->dfLRX, &psOptions->dfLRY)) )
928 {
929 OGRCoordinateTransformation::DestroyCT(poCT);
930
931 CPLError( CE_Failure, CPLE_AppDefined, "-projwin_srs ignored since coordinate transformation failed.");
932 GDALTranslateOptionsFree(psOptions);
933 return nullptr;
934 }
935 delete poCT;
936 }
937 }
938 else
939 {
940 CPLError( CE_None, CPLE_None, "-projwin_srs ignored since the dataset has no projection.");
941 }
942 }
943
944 psOptions->adfSrcWin[0] = (psOptions->dfULX - adfGeoTransform[0]) / adfGeoTransform[1];
945 psOptions->adfSrcWin[1] = (psOptions->dfULY - adfGeoTransform[3]) / adfGeoTransform[5];
946
947 psOptions->adfSrcWin[2] = (psOptions->dfLRX - psOptions->dfULX) / adfGeoTransform[1];
948 psOptions->adfSrcWin[3] = (psOptions->dfLRY - psOptions->dfULY) / adfGeoTransform[5];
949
950 // In case of nearest resampling, round to integer pixels (#6610)
951 if( psOptions->pszResampling == nullptr ||
952 EQUALN(psOptions->pszResampling, "NEAR", 4) )
953 {
954 psOptions->adfSrcWin[0] = floor(psOptions->adfSrcWin[0] + 0.001);
955 psOptions->adfSrcWin[1] = floor(psOptions->adfSrcWin[1] + 0.001);
956 psOptions->adfSrcWin[2] = floor(psOptions->adfSrcWin[2] + 0.5);
957 psOptions->adfSrcWin[3] = floor(psOptions->adfSrcWin[3] + 0.5);
958 }
959
960 /*if( !bQuiet )
961 fprintf( stdout,
962 "Computed -srcwin %g %g %g %g from projected window.\n",
963 adfSrcWin[0],
964 adfSrcWin[1],
965 adfSrcWin[2],
966 adfSrcWin[3] ); */
967 }
968
969 /* -------------------------------------------------------------------- */
970 /* Verify source window dimensions. */
971 /* -------------------------------------------------------------------- */
972 if( psOptions->adfSrcWin[2] <= 0 || psOptions->adfSrcWin[3] <= 0 )
973 {
974 CPLError( CE_Failure, CPLE_AppDefined,
975 "Error: %s-srcwin %g %g %g %g has negative width and/or height.",
976 ( psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 || psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0 ) ? "Computed " : "",
977 psOptions->adfSrcWin[0],
978 psOptions->adfSrcWin[1],
979 psOptions->adfSrcWin[2],
980 psOptions->adfSrcWin[3] );
981 GDALTranslateOptionsFree(psOptions);
982 return nullptr;
983 }
984
985 /* -------------------------------------------------------------------- */
986 /* Verify source window dimensions. */
987 /* -------------------------------------------------------------------- */
988 else if( psOptions->adfSrcWin[0] <= -1 || psOptions->adfSrcWin[1] <= -1
989 || psOptions->adfSrcWin[0] + psOptions->adfSrcWin[2] >= GDALGetRasterXSize(hSrcDataset) + 1
990 || psOptions->adfSrcWin[1] + psOptions->adfSrcWin[3] >= GDALGetRasterYSize(hSrcDataset) + 1 )
991 {
992 const bool bCompletelyOutside =
993 psOptions->adfSrcWin[0] + psOptions->adfSrcWin[2] <= 0 ||
994 psOptions->adfSrcWin[1] + psOptions->adfSrcWin[3] <= 0 ||
995 psOptions->adfSrcWin[0] >= GDALGetRasterXSize(hSrcDataset) ||
996 psOptions->adfSrcWin[1] >= GDALGetRasterYSize(hSrcDataset);
997 const bool bIsError =
998 psOptions->bErrorOnPartiallyOutside ||
999 (bCompletelyOutside && psOptions->bErrorOnCompletelyOutside);
1000 if( !psOptions->bQuiet || bIsError )
1001 {
1002 CPLErr eErr = bIsError ? CE_Failure : CE_Warning;
1003
1004 CPLError( eErr, CPLE_AppDefined,
1005 "%s-srcwin %g %g %g %g falls %s outside raster extent.%s",
1006 ( psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 || psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0 ) ? "Computed " : "",
1007 psOptions->adfSrcWin[0],
1008 psOptions->adfSrcWin[1],
1009 psOptions->adfSrcWin[2],
1010 psOptions->adfSrcWin[3],
1011 bCompletelyOutside ? "completely" : "partially",
1012 bIsError ? "" : " Going on however." );
1013 }
1014 if( bIsError )
1015 {
1016 GDALTranslateOptionsFree(psOptions);
1017 return nullptr;
1018 }
1019 }
1020
1021 /* -------------------------------------------------------------------- */
1022 /* Find the output driver. */
1023 /* -------------------------------------------------------------------- */
1024 if( psOptions->pszFormat == nullptr )
1025 {
1026 CPLString osFormat = GetOutputDriverForRaster(pszDest);
1027 if( osFormat.empty() )
1028 {
1029 GDALTranslateOptionsFree(psOptions);
1030 return nullptr;
1031 }
1032 psOptions->pszFormat = CPLStrdup(osFormat);
1033 }
1034
1035 GDALDriverH hDriver = GDALGetDriverByName(psOptions->pszFormat);
1036 if( hDriver == nullptr )
1037 {
1038 CPLError( CE_Failure, CPLE_IllegalArg, "Output driver `%s' not recognised.",
1039 psOptions->pszFormat);
1040 GDALTranslateOptionsFree(psOptions);
1041 return nullptr;
1042 }
1043
1044 /* -------------------------------------------------------------------- */
1045 /* Make sure we cleanup if there is an existing dataset of this */
1046 /* name. But even if that seems to fail we will continue since */
1047 /* it might just be a corrupt file or something. */
1048 /* This is needed for */
1049 /* gdal_translate foo.tif foo.tif.ovr -outsize 50% 50% */
1050 /* -------------------------------------------------------------------- */
1051 if( !CPLFetchBool(psOptions->papszCreateOptions, "APPEND_SUBDATASET", false) )
1052 {
1053 // Someone issuing Create("foo.tif") on a
1054 // memory driver doesn't expect files with those names to be deleted
1055 // on a file system...
1056 // This is somewhat messy. Ideally there should be a way for the
1057 // driver to overload the default behavior
1058 if( !EQUAL(psOptions->pszFormat, "MEM") &&
1059 !EQUAL(psOptions->pszFormat, "Memory") )
1060 {
1061 GDALDriver::FromHandle(hDriver)->QuietDelete( pszDest );
1062 }
1063 // Make sure to load early overviews, so that on the GTiff driver
1064 // external .ovr is looked for before it might be created as the
1065 // output dataset !
1066 if( GDALGetRasterCount( hSrcDataset ) )
1067 {
1068 auto hBand = GDALGetRasterBand(hSrcDataset, 1);
1069 if( hBand )
1070 GDALGetOverviewCount(hBand);
1071 }
1072 }
1073
1074 char** papszDriverMD = GDALGetMetadata(hDriver, nullptr);
1075
1076 if( !CPLTestBool( CSLFetchNameValueDef(papszDriverMD,
1077 GDAL_DCAP_RASTER, "FALSE") ) )
1078 {
1079 CPLError( CE_Failure, CPLE_AppDefined,
1080 "%s driver has no raster capabilities.",
1081 psOptions->pszFormat );
1082 GDALTranslateOptionsFree(psOptions);
1083 return nullptr;
1084 }
1085
1086 if( !CPLTestBool( CSLFetchNameValueDef(papszDriverMD,
1087 GDAL_DCAP_CREATE, "FALSE") ) &&
1088 !CPLTestBool( CSLFetchNameValueDef(papszDriverMD,
1089 GDAL_DCAP_CREATECOPY, "FALSE") ))
1090 {
1091 CPLError( CE_Failure, CPLE_AppDefined,
1092 "%s driver has no creation capabilities.",
1093 psOptions->pszFormat );
1094 GDALTranslateOptionsFree(psOptions);
1095 return nullptr;
1096 }
1097
1098 /* -------------------------------------------------------------------- */
1099 /* The short form is to CreateCopy(). We use this if the input */
1100 /* matches the whole dataset. Eventually we should rewrite */
1101 /* this entire program to use virtual datasets to construct a */
1102 /* virtual input source to copy from. */
1103 /* -------------------------------------------------------------------- */
1104
1105 const bool bSpatialArrangementPreserved =
1106 psOptions->adfSrcWin[0] == 0 && psOptions->adfSrcWin[1] == 0 &&
1107 psOptions->adfSrcWin[2] == GDALGetRasterXSize(hSrcDataset) &&
1108 psOptions->adfSrcWin[3] == GDALGetRasterYSize(hSrcDataset) &&
1109 psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
1110 psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0 &&
1111 psOptions->dfXRes == 0.0;
1112
1113 if( psOptions->eOutputType == GDT_Unknown
1114 && psOptions->nScaleRepeat == 0 && psOptions->nExponentRepeat == 0 && !psOptions->bUnscale
1115 && !psOptions->bSetScale && !psOptions->bSetOffset
1116 && CSLCount(psOptions->papszMetadataOptions) == 0 && bAllBandsInOrder
1117 && psOptions->eMaskMode == MASK_AUTO
1118 && bSpatialArrangementPreserved
1119 && !psOptions->bNoGCP
1120 && psOptions->nGCPCount == 0 && !bGotBounds
1121 && psOptions->pszOutputSRS == nullptr && !psOptions->bSetNoData && !psOptions->bUnsetNoData
1122 && psOptions->nRGBExpand == 0 && !psOptions->bNoRAT
1123 && psOptions->panColorInterp == nullptr
1124 && !psOptions->bNoXMP )
1125 {
1126
1127 // For gdal_translate_fuzzer
1128 if( psOptions->nLimitOutSize > 0 )
1129 {
1130 vsi_l_offset nRawOutSize =
1131 static_cast<vsi_l_offset>(GDALGetRasterXSize(hSrcDataset)) *
1132 GDALGetRasterYSize(hSrcDataset) *
1133 psOptions->nBandCount;
1134 if( psOptions->nBandCount )
1135 {
1136 nRawOutSize *= GDALGetDataTypeSizeBytes(
1137 static_cast<GDALDataset *>(hSrcDataset)->GetRasterBand(1)->GetRasterDataType() );
1138 }
1139 if( nRawOutSize > static_cast<vsi_l_offset>(psOptions->nLimitOutSize) )
1140 {
1141 CPLError( CE_Failure, CPLE_IllegalArg,
1142 "Attempt to create %dx%d dataset is above authorized limit.",
1143 GDALGetRasterXSize(hSrcDataset),
1144 GDALGetRasterYSize(hSrcDataset) );
1145 GDALTranslateOptionsFree(psOptions);
1146 return nullptr;
1147 }
1148 }
1149
1150 /* -------------------------------------------------------------------- */
1151 /* Compute stats if required. */
1152 /* -------------------------------------------------------------------- */
1153 if (psOptions->bStats)
1154 {
1155 GDALDataset* poSrcDS = GDALDataset::FromHandle(hSrcDataset);
1156 for( int i = 0; i < poSrcDS->GetRasterCount(); i++ )
1157 {
1158 double dfMin, dfMax, dfMean, dfStdDev;
1159 poSrcDS->GetRasterBand(i+1)->ComputeStatistics(
1160 psOptions->bApproxStats,
1161 &dfMin, &dfMax, &dfMean, &dfStdDev,
1162 GDALDummyProgress, nullptr );
1163 }
1164 }
1165
1166 hOutDS = GDALCreateCopy( hDriver, pszDest, hSrcDataset,
1167 psOptions->bStrict, psOptions->papszCreateOptions,
1168 psOptions->pfnProgress, psOptions->pProgressData );
1169 hOutDS = GDALTranslateFlush(hOutDS);
1170
1171 GDALTranslateOptionsFree(psOptions);
1172 return hOutDS;
1173 }
1174
1175 if( CSLFetchNameValue(psOptions->papszCreateOptions, "COPY_SRC_OVERVIEWS") )
1176 {
1177 CPLError(CE_Warning, CPLE_AppDefined,
1178 "General options of gdal_translate make the "
1179 "COPY_SRC_OVERVIEWS creation option ineffective as they hide "
1180 "the overviews");
1181 }
1182
1183 /* -------------------------------------------------------------------- */
1184 /* Establish some parameters. */
1185 /* -------------------------------------------------------------------- */
1186 int nOXSize = 0;
1187 int nOYSize = 0;
1188
1189 bool bHasSrcGeoTransform = false;
1190 double adfSrcGeoTransform[6] = {};
1191 if( GDALGetGeoTransform( hSrcDataset, adfSrcGeoTransform ) == CE_None )
1192 bHasSrcGeoTransform = true;
1193
1194 if( psOptions->dfXRes != 0.0 )
1195 {
1196 if( !(bHasSrcGeoTransform &&
1197 psOptions->nGCPCount == 0 &&
1198 adfSrcGeoTransform[2] == 0.0 && adfSrcGeoTransform[4] == 0.0) )
1199 {
1200 CPLError( CE_Failure, CPLE_IllegalArg,
1201 "The -tr option was used, but there's no geotransform or it is\n"
1202 "rotated. This configuration is not supported." );
1203 GDALTranslateOptionsFree(psOptions);
1204 return nullptr;
1205 }
1206 const double dfOXSize =
1207 psOptions->adfSrcWin[2] /
1208 psOptions->dfXRes * adfSrcGeoTransform[1] + 0.5;
1209 const double dfOYSize =
1210 psOptions->adfSrcWin[3] /
1211 psOptions->dfYRes * fabs(adfSrcGeoTransform[5]) + 0.5;
1212 if( dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
1213 dfOYSize < 1 || !GDALIsValueInRange<int>(dfOXSize) )
1214 {
1215 CPLError(CE_Failure, CPLE_IllegalArg,
1216 "Invalid output size: %g x %g",
1217 dfOXSize, dfOYSize);
1218 GDALTranslateOptionsFree(psOptions);
1219 return nullptr;
1220 }
1221 nOXSize = static_cast<int>(dfOXSize);
1222 nOYSize = static_cast<int>(dfOYSize);
1223 }
1224 else if( psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 && psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0)
1225 {
1226 double dfOXSize = ceil(psOptions->adfSrcWin[2]-0.001);
1227 double dfOYSize = ceil(psOptions->adfSrcWin[3]-0.001);
1228 if( dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
1229 dfOYSize < 1 || !GDALIsValueInRange<int>(dfOXSize) )
1230 {
1231 CPLError(CE_Failure, CPLE_IllegalArg,
1232 "Invalid output size: %g x %g",
1233 dfOXSize, dfOYSize);
1234 GDALTranslateOptionsFree(psOptions);
1235 return nullptr;
1236 }
1237 nOXSize = static_cast<int>(dfOXSize);
1238 nOYSize = static_cast<int>(dfOYSize);
1239 }
1240 else
1241 {
1242 if( !(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0) )
1243 {
1244 if(psOptions->nOXSizePixel != 0)
1245 nOXSize = psOptions->nOXSizePixel;
1246 else
1247 {
1248 const double dfOXSize =
1249 psOptions->dfOXSizePct / 100 * psOptions->adfSrcWin[2];
1250 if( dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) )
1251 {
1252 CPLError(CE_Failure, CPLE_IllegalArg,
1253 "Invalid output width: %g",
1254 dfOXSize);
1255 GDALTranslateOptionsFree(psOptions);
1256 return nullptr;
1257 }
1258 nOXSize = static_cast<int>(dfOXSize);
1259 }
1260 }
1261
1262 if( !(psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0) )
1263 {
1264 if(psOptions->nOYSizePixel != 0)
1265 nOYSize = psOptions->nOYSizePixel;
1266 else
1267 {
1268 const double dfOYSize =
1269 psOptions->dfOYSizePct / 100 * psOptions->adfSrcWin[3];
1270 if( dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize) )
1271 {
1272 CPLError(CE_Failure, CPLE_IllegalArg,
1273 "Invalid output height: %g",
1274 dfOYSize);
1275 GDALTranslateOptionsFree(psOptions);
1276 return nullptr;
1277 }
1278 nOYSize = static_cast<int>(dfOYSize);
1279 }
1280 }
1281
1282 if( psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 )
1283 {
1284 const double dfOXSize =
1285 static_cast<double>(nOYSize) * psOptions->adfSrcWin[2] /
1286 psOptions->adfSrcWin[3] + 0.5;
1287 if( dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) )
1288 {
1289 CPLError(CE_Failure, CPLE_IllegalArg,
1290 "Invalid output width: %g",
1291 dfOXSize);
1292 GDALTranslateOptionsFree(psOptions);
1293 return nullptr;
1294 }
1295 nOXSize = static_cast<int>(dfOXSize);
1296 }
1297 else if( psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0 )
1298 {
1299 const double dfOYSize =
1300 static_cast<double>(nOXSize) * psOptions->adfSrcWin[3] /
1301 psOptions->adfSrcWin[2] + 0.5;
1302 if( dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize) )
1303 {
1304 CPLError(CE_Failure, CPLE_IllegalArg,
1305 "Invalid output height: %g",
1306 dfOYSize);
1307 GDALTranslateOptionsFree(psOptions);
1308 return nullptr;
1309 }
1310 nOYSize = static_cast<int>(dfOYSize);
1311 }
1312 }
1313
1314 if( nOXSize <= 0 || nOYSize <= 0 )
1315 {
1316 CPLError( CE_Failure, CPLE_IllegalArg, "Attempt to create %dx%d dataset is illegal.", nOXSize, nOYSize);
1317 GDALTranslateOptionsFree(psOptions);
1318 return nullptr;
1319 }
1320
1321 // For gdal_translate_fuzzer
1322 if( psOptions->nLimitOutSize > 0 )
1323 {
1324 vsi_l_offset nRawOutSize = static_cast<vsi_l_offset>(nOXSize) * nOYSize;
1325 if( psOptions->nBandCount )
1326 {
1327 if( nRawOutSize > std::numeric_limits<vsi_l_offset>::max() / psOptions->nBandCount )
1328 {
1329 GDALTranslateOptionsFree(psOptions);
1330 return nullptr;
1331 }
1332 nRawOutSize *= psOptions->nBandCount;
1333 const int nDTSize = GDALGetDataTypeSizeBytes(
1334 static_cast<GDALDataset *>(hSrcDataset)->GetRasterBand(1)->GetRasterDataType() );
1335 if( nDTSize > 0 &&
1336 nRawOutSize > std::numeric_limits<vsi_l_offset>::max() / nDTSize )
1337 {
1338 GDALTranslateOptionsFree(psOptions);
1339 return nullptr;
1340 }
1341 nRawOutSize *= nDTSize;
1342 }
1343 if( nRawOutSize > static_cast<vsi_l_offset>(psOptions->nLimitOutSize) )
1344 {
1345 CPLError( CE_Failure, CPLE_IllegalArg,
1346 "Attempt to create %dx%d dataset is above authorized limit.",
1347 nOXSize, nOYSize);
1348 GDALTranslateOptionsFree(psOptions);
1349 return nullptr;
1350 }
1351 }
1352
1353
1354 /* ==================================================================== */
1355 /* Create a virtual dataset. */
1356 /* ==================================================================== */
1357
1358 /* -------------------------------------------------------------------- */
1359 /* Make a virtual clone. */
1360 /* -------------------------------------------------------------------- */
1361 VRTDataset *poVDS = static_cast<VRTDataset *>(VRTCreate(nOXSize, nOYSize));
1362
1363 if( psOptions->nGCPCount == 0 )
1364 {
1365 if( psOptions->pszOutputSRS != nullptr )
1366 {
1367 poVDS->SetProjection( psOptions->pszOutputSRS );
1368 }
1369 else
1370 {
1371 pszProjection = GDALGetProjectionRef( hSrcDataset );
1372 if( pszProjection != nullptr && strlen(pszProjection) > 0 )
1373 poVDS->SetProjection( pszProjection );
1374 }
1375 }
1376
1377 bool bHasDstGeoTransform = false;
1378 double adfDstGeoTransform[6] = {};
1379
1380 if( bGotBounds )
1381 {
1382 bHasDstGeoTransform = true;
1383 adfDstGeoTransform[0] = psOptions->adfULLR[0];
1384 adfDstGeoTransform[1] = (psOptions->adfULLR[2] - psOptions->adfULLR[0]) / nOXSize;
1385 adfDstGeoTransform[2] = 0.0;
1386 adfDstGeoTransform[3] = psOptions->adfULLR[1];
1387 adfDstGeoTransform[4] = 0.0;
1388 adfDstGeoTransform[5] = (psOptions->adfULLR[3] - psOptions->adfULLR[1]) / nOYSize;
1389
1390 poVDS->SetGeoTransform( adfDstGeoTransform );
1391 }
1392
1393 else if( bHasSrcGeoTransform && psOptions->nGCPCount == 0 )
1394 {
1395 bHasDstGeoTransform = true;
1396 memcpy( adfDstGeoTransform, adfSrcGeoTransform, 6 * sizeof(double) );
1397 adfDstGeoTransform[0] += psOptions->adfSrcWin[0] * adfDstGeoTransform[1]
1398 + psOptions->adfSrcWin[1] * adfDstGeoTransform[2];
1399 adfDstGeoTransform[3] += psOptions->adfSrcWin[0] * adfDstGeoTransform[4]
1400 + psOptions->adfSrcWin[1] * adfDstGeoTransform[5];
1401
1402 const double dfX = static_cast<double>(nOXSize);
1403 const double dfY = static_cast<double>(nOYSize);
1404 adfDstGeoTransform[1] *= psOptions->adfSrcWin[2] / dfX;
1405 adfDstGeoTransform[2] *= psOptions->adfSrcWin[3] / dfY;
1406 adfDstGeoTransform[4] *= psOptions->adfSrcWin[2] / dfX;
1407 adfDstGeoTransform[5] *= psOptions->adfSrcWin[3] / dfY;
1408
1409 if( psOptions->dfXRes != 0.0 )
1410 {
1411 adfDstGeoTransform[1] = psOptions->dfXRes;
1412 adfDstGeoTransform[5] = (adfDstGeoTransform[5] > 0) ? psOptions->dfYRes : -psOptions->dfYRes;
1413 }
1414
1415 poVDS->SetGeoTransform( adfDstGeoTransform );
1416 }
1417
1418 if( psOptions->nGCPCount != 0 )
1419 {
1420 const char *pszGCPProjection = psOptions->pszOutputSRS;
1421
1422 if( pszGCPProjection == nullptr )
1423 pszGCPProjection = GDALGetGCPProjection( hSrcDataset );
1424 if( pszGCPProjection == nullptr )
1425 pszGCPProjection = "";
1426
1427 poVDS->SetGCPs( psOptions->nGCPCount, psOptions->pasGCPs, pszGCPProjection );
1428 }
1429
1430 else if( !psOptions->bNoGCP && GDALGetGCPCount( hSrcDataset ) > 0 )
1431 {
1432 const int nGCPs = GDALGetGCPCount(hSrcDataset);
1433
1434 GDAL_GCP *pasGCPs = GDALDuplicateGCPs(nGCPs, GDALGetGCPs(hSrcDataset));
1435
1436 for( int i = 0; i < nGCPs; i++ )
1437 {
1438 pasGCPs[i].dfGCPPixel -= psOptions->adfSrcWin[0];
1439 pasGCPs[i].dfGCPLine -= psOptions->adfSrcWin[1];
1440 pasGCPs[i].dfGCPPixel *=
1441 nOXSize / static_cast<double>(psOptions->adfSrcWin[2]);
1442 pasGCPs[i].dfGCPLine *=
1443 nOYSize / static_cast<double>(psOptions->adfSrcWin[3]);
1444 }
1445
1446 poVDS->SetGCPs( nGCPs, pasGCPs,
1447 GDALGetGCPProjection( hSrcDataset ) );
1448
1449 GDALDeinitGCPs( nGCPs, pasGCPs );
1450 CPLFree( pasGCPs );
1451 }
1452
1453 /* -------------------------------------------------------------------- */
1454 /* To make the VRT to look less awkward (but this is optional */
1455 /* in fact), avoid negative values. */
1456 /* -------------------------------------------------------------------- */
1457 double adfDstWin[4] =
1458 {0.0, 0.0, static_cast<double>(nOXSize), static_cast<double>(nOYSize)};
1459
1460 // When specifying -tr with non-nearest resampling, make sure that the
1461 // size of target window precisely matches the requested resolution, to
1462 // avoid any shift.
1463 if( bHasSrcGeoTransform && bHasDstGeoTransform &&
1464 psOptions->dfXRes != 0.0 &&
1465 psOptions->pszResampling != nullptr &&
1466 !EQUALN(psOptions->pszResampling, "NEAR", 4) )
1467 {
1468 adfDstWin[2] = psOptions->adfSrcWin[2] * adfSrcGeoTransform[1] / adfDstGeoTransform[1];
1469 adfDstWin[3] = psOptions->adfSrcWin[3] * fabs(adfSrcGeoTransform[5] / adfDstGeoTransform[5]);
1470 }
1471
1472 double adfSrcWinOri[4];
1473 static_assert(sizeof(adfSrcWinOri) == sizeof(psOptions->adfSrcWin),
1474 "inconsistent adfSrcWin size");
1475 memcpy(adfSrcWinOri, psOptions->adfSrcWin, sizeof(psOptions->adfSrcWin));
1476 FixSrcDstWindow( psOptions->adfSrcWin, adfDstWin,
1477 GDALGetRasterXSize(hSrcDataset),
1478 GDALGetRasterYSize(hSrcDataset) );
1479
1480 /* -------------------------------------------------------------------- */
1481 /* Transfer generally applicable metadata. */
1482 /* -------------------------------------------------------------------- */
1483 GDALDataset* poSrcDS = GDALDataset::FromHandle(hSrcDataset);
1484 char** papszMetadata = CSLDuplicate(poSrcDS->GetMetadata());
1485 if ( psOptions->nScaleRepeat > 0 || psOptions->bUnscale || psOptions->eOutputType != GDT_Unknown )
1486 {
1487 /* Remove TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */
1488 /* if the data range may change because of options */
1489 char** papszIter = papszMetadata;
1490 while(papszIter && *papszIter)
1491 {
1492 if (STARTS_WITH_CI(*papszIter, "TIFFTAG_MINSAMPLEVALUE=") ||
1493 STARTS_WITH_CI(*papszIter, "TIFFTAG_MAXSAMPLEVALUE="))
1494 {
1495 CPLFree(*papszIter);
1496 memmove(papszIter, papszIter+1, sizeof(char*) * (CSLCount(papszIter+1)+1));
1497 }
1498 else
1499 papszIter++;
1500 }
1501 }
1502
1503 // Remove NITF_BLOCKA_ stuff if georeferencing is changed
1504 if( !(psOptions->adfSrcWin[0] == 0 && psOptions->adfSrcWin[1] == 0 &&
1505 psOptions->adfSrcWin[2] == GDALGetRasterXSize(hSrcDataset) &&
1506 psOptions->adfSrcWin[3] == GDALGetRasterYSize(hSrcDataset) &&
1507 psOptions->nGCPCount == 0 && !bGotBounds) )
1508 {
1509 char** papszIter = papszMetadata;
1510 while(papszIter && *papszIter)
1511 {
1512 if (STARTS_WITH_CI(*papszIter, "NITF_BLOCKA_"))
1513 {
1514 CPLFree(*papszIter);
1515 memmove(papszIter, papszIter+1, sizeof(char*) * (CSLCount(papszIter+1)+1));
1516 }
1517 else
1518 papszIter++;
1519 }
1520 }
1521
1522 {
1523 char** papszIter = papszMetadata;
1524 while(papszIter && *papszIter)
1525 {
1526 // Do not preserve the CACHE_PATH from the WMS driver
1527 if (STARTS_WITH_CI(*papszIter, "CACHE_PATH="))
1528 {
1529 CPLFree(*papszIter);
1530 memmove(papszIter, papszIter+1, sizeof(char*) * (CSLCount(papszIter+1)+1));
1531 }
1532 else
1533 papszIter++;
1534 }
1535 }
1536
1537 poVDS->SetMetadata( papszMetadata );
1538 CSLDestroy( papszMetadata );
1539 AttachMetadata( static_cast<GDALDatasetH>(poVDS), psOptions->papszMetadataOptions );
1540
1541 const char* pszInterleave = GDALGetMetadataItem(hSrcDataset, "INTERLEAVE", "IMAGE_STRUCTURE");
1542 if (pszInterleave)
1543 poVDS->SetMetadataItem("INTERLEAVE", pszInterleave, "IMAGE_STRUCTURE");
1544
1545 {
1546 const char* pszCompression = poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
1547 if( pszCompression )
1548 {
1549 poVDS->SetMetadataItem("COMPRESSION", pszCompression, "IMAGE_STRUCTURE");
1550 }
1551 }
1552
1553 /* ISIS3 metadata preservation */
1554 char** papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
1555 if( papszMD_ISIS3 != nullptr)
1556 {
1557 if( !bAllBandsInOrder )
1558 {
1559 CPLString osJSON = EditISIS3MetadataForBandChange(
1560 papszMD_ISIS3[0], GDALGetRasterCount( hSrcDataset ), psOptions);
1561 if( !osJSON.empty() )
1562 {
1563 char* apszMD[] = { &osJSON[0], nullptr };
1564 poVDS->SetMetadata( apszMD, "json:ISIS3" );
1565 }
1566 }
1567 else
1568 {
1569 poVDS->SetMetadata( papszMD_ISIS3, "json:ISIS3" );
1570 }
1571 }
1572
1573 // PDS4 -> PDS4 special case
1574 if( EQUAL(psOptions->pszFormat, "PDS4") )
1575 {
1576 char** papszMD_PDS4 = poSrcDS->GetMetadata("xml:PDS4");
1577 if( papszMD_PDS4 != nullptr)
1578 poVDS->SetMetadata( papszMD_PDS4, "xml:PDS4" );
1579 }
1580
1581 // VICAR -> VICAR special case
1582 if( EQUAL(psOptions->pszFormat, "VICAR") )
1583 {
1584 char** papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
1585 if( papszMD_VICAR != nullptr)
1586 poVDS->SetMetadata( papszMD_VICAR, "json:VICAR" );
1587 }
1588
1589 // Copy XMP metadata
1590 if( !psOptions->bNoXMP )
1591 {
1592 char** papszXMP = poSrcDS->GetMetadata("xml:XMP");
1593 if (papszXMP != nullptr && *papszXMP != nullptr)
1594 {
1595 poVDS->SetMetadata(papszXMP, "xml:XMP");
1596 }
1597 }
1598
1599
1600 /* -------------------------------------------------------------------- */
1601 /* Transfer metadata that remains valid if the spatial */
1602 /* arrangement of the data is unaltered. */
1603 /* -------------------------------------------------------------------- */
1604 if( bSpatialArrangementPreserved )
1605 {
1606 char **papszMD = poSrcDS->GetMetadata("RPC");
1607 if( papszMD != nullptr )
1608 poVDS->SetMetadata( papszMD, "RPC" );
1609
1610 papszMD = poSrcDS->GetMetadata("GEOLOCATION");
1611 if( papszMD != nullptr )
1612 poVDS->SetMetadata( papszMD, "GEOLOCATION" );
1613 }
1614 else
1615 {
1616 char **papszMD = poSrcDS->GetMetadata("RPC");
1617 if( papszMD != nullptr )
1618 {
1619 papszMD = CSLDuplicate(papszMD);
1620
1621 double dfSAMP_OFF = CPLAtof(CSLFetchNameValueDef(papszMD, "SAMP_OFF", "0"));
1622 double dfLINE_OFF = CPLAtof(CSLFetchNameValueDef(papszMD, "LINE_OFF", "0"));
1623 double dfSAMP_SCALE = CPLAtof(CSLFetchNameValueDef(papszMD, "SAMP_SCALE", "1"));
1624 double dfLINE_SCALE = CPLAtof(CSLFetchNameValueDef(papszMD, "LINE_SCALE", "1"));
1625
1626 dfSAMP_OFF -= adfSrcWinOri[0];
1627 dfLINE_OFF -= adfSrcWinOri[1];
1628
1629 const double df2 = adfSrcWinOri[2];
1630 const double df3 = adfSrcWinOri[3];
1631 dfSAMP_OFF *= nOXSize / df2;
1632 dfLINE_OFF *= nOYSize / df3;
1633 dfSAMP_SCALE *= nOXSize / df2;
1634 dfLINE_SCALE *= nOYSize / df3;
1635
1636 CPLString osField;
1637 osField.Printf( "%.15g", dfLINE_OFF );
1638 papszMD = CSLSetNameValue( papszMD, "LINE_OFF", osField );
1639
1640 osField.Printf( "%.15g", dfSAMP_OFF );
1641 papszMD = CSLSetNameValue( papszMD, "SAMP_OFF", osField );
1642
1643 osField.Printf( "%.15g", dfLINE_SCALE );
1644 papszMD = CSLSetNameValue( papszMD, "LINE_SCALE", osField );
1645
1646 osField.Printf( "%.15g", dfSAMP_SCALE );
1647 papszMD = CSLSetNameValue( papszMD, "SAMP_SCALE", osField );
1648
1649 poVDS->SetMetadata( papszMD, "RPC" );
1650 CSLDestroy(papszMD);
1651 }
1652 }
1653
1654 const int nSrcBandCount = psOptions->nBandCount;
1655
1656 if (psOptions->nRGBExpand != 0)
1657 {
1658 GDALRasterBand *poSrcBand =
1659 static_cast<GDALDataset*>(hSrcDataset)->
1660 GetRasterBand(std::abs(psOptions->panBandList[0]));
1661 if (psOptions->panBandList[0] < 0)
1662 poSrcBand = poSrcBand->GetMaskBand();
1663 GDALColorTable* poColorTable = poSrcBand->GetColorTable();
1664 if (poColorTable == nullptr)
1665 {
1666 CPLError(CE_Failure, CPLE_AppDefined,
1667 "Error : band %d has no color table",
1668 std::abs(psOptions->panBandList[0]));
1669 GDALClose(poVDS);
1670 GDALTranslateOptionsFree(psOptions);
1671 return nullptr;
1672 }
1673
1674 /* Check that the color table only contains gray levels */
1675 /* when using -expand gray */
1676 if (psOptions->nRGBExpand == 1)
1677 {
1678 int nColorCount = poColorTable->GetColorEntryCount();
1679 for( int nColor = 0; nColor < nColorCount; nColor++ )
1680 {
1681 const GDALColorEntry* poEntry = poColorTable->GetColorEntry(nColor);
1682 if (poEntry->c1 != poEntry->c2 || poEntry->c1 != poEntry->c3)
1683 {
1684 CPLError( CE_Warning, CPLE_AppDefined, "Warning : color table contains non gray levels colors");
1685 break;
1686 }
1687 }
1688 }
1689
1690 if (psOptions->nBandCount == 1)
1691 {
1692 psOptions->nBandCount = psOptions->nRGBExpand;
1693 }
1694 else if (psOptions->nBandCount == 2 && (psOptions->nRGBExpand == 3 || psOptions->nRGBExpand == 4))
1695 {
1696 psOptions->nBandCount = psOptions->nRGBExpand;
1697 }
1698 else
1699 {
1700 CPLError( CE_Failure, CPLE_IllegalArg, "Error : invalid use of -expand option.");
1701 GDALClose(poVDS);
1702 GDALTranslateOptionsFree(psOptions);
1703 return nullptr;
1704 }
1705 }
1706
1707 // Can be set to TRUE in the band loop too
1708 bool bFilterOutStatsMetadata =
1709 psOptions->nScaleRepeat > 0 || psOptions->bUnscale ||
1710 !bSpatialArrangementPreserved || psOptions->nRGBExpand != 0;
1711
1712 if( psOptions->nColorInterpSize > psOptions->nBandCount )
1713 {
1714 CPLError(CE_Warning, CPLE_AppDefined,
1715 "More bands defined in -colorinterp than output bands");
1716 }
1717
1718 /* ==================================================================== */
1719 /* Process all bands. */
1720 /* ==================================================================== */
1721 for( int i = 0; i < psOptions->nBandCount; i++ )
1722 {
1723 int nComponent = 0;
1724 int nSrcBand = 0;
1725
1726 if (psOptions->nRGBExpand != 0)
1727 {
1728 if (nSrcBandCount == 2 && psOptions->nRGBExpand == 4 && i == 3)
1729 nSrcBand = psOptions->panBandList[1];
1730 else
1731 {
1732 nSrcBand = psOptions->panBandList[0];
1733 nComponent = i + 1;
1734 }
1735 }
1736 else
1737 {
1738 nSrcBand = psOptions->panBandList[i];
1739 }
1740
1741 GDALRasterBand *poSrcBand =
1742 static_cast<GDALDataset*>(hSrcDataset)->GetRasterBand(std::abs(nSrcBand));
1743
1744 /* -------------------------------------------------------------------- */
1745 /* Select output data type to match source. */
1746 /* -------------------------------------------------------------------- */
1747 GDALRasterBand* poRealSrcBand =
1748 (nSrcBand < 0) ? poSrcBand->GetMaskBand(): poSrcBand;
1749 GDALDataType eBandType;
1750 if( psOptions->eOutputType == GDT_Unknown )
1751 {
1752 eBandType = poRealSrcBand->GetRasterDataType();
1753 }
1754 else
1755 {
1756 eBandType = psOptions->eOutputType;
1757
1758 // Check that we can copy existing statistics
1759 GDALDataType eSrcBandType = poRealSrcBand->GetRasterDataType();
1760 const char* pszMin = poRealSrcBand->GetMetadataItem("STATISTICS_MINIMUM");
1761 const char* pszMax = poRealSrcBand->GetMetadataItem("STATISTICS_MAXIMUM");
1762 if( !bFilterOutStatsMetadata && eBandType != eSrcBandType &&
1763 pszMin != nullptr && pszMax != nullptr )
1764 {
1765 const bool bSrcIsInteger =
1766 CPL_TO_BOOL(GDALDataTypeIsInteger(eSrcBandType) &&
1767 !GDALDataTypeIsComplex(eSrcBandType));
1768 const bool bDstIsInteger =
1769 CPL_TO_BOOL(GDALDataTypeIsInteger(eBandType) &&
1770 !GDALDataTypeIsComplex(eBandType));
1771 if( bSrcIsInteger && bDstIsInteger )
1772 {
1773 GInt32 nDstMin = 0;
1774 GUInt32 nDstMax = 0;
1775 switch( eBandType )
1776 {
1777 case GDT_Byte:
1778 nDstMin = 0;
1779 nDstMax = 255;
1780 break;
1781 case GDT_UInt16:
1782 nDstMin = 0;
1783 nDstMax = 65535;
1784 break;
1785 case GDT_Int16:
1786 nDstMin = -32768;
1787 nDstMax = 32767;
1788 break;
1789 case GDT_UInt32:
1790 nDstMin = 0;
1791 nDstMax = 0xFFFFFFFFU;
1792 break;
1793 case GDT_Int32:
1794 nDstMin = 0x80000000;
1795 nDstMax = 0x7FFFFFFF;
1796 break;
1797 default:
1798 CPLAssert(false);
1799 break;
1800 }
1801
1802 GInt32 nMin = atoi(pszMin);
1803 GUInt32 nMax = static_cast<GUInt32>(strtoul(pszMax, nullptr, 10));
1804 if( nMin < nDstMin || nMax > nDstMax )
1805 bFilterOutStatsMetadata = true;
1806 }
1807 // Float64 is large enough to hold all integer <= 32 bit or float32 values
1808 // there might be other OK cases, but ere on safe side for now
1809 else if( !((bSrcIsInteger || eSrcBandType == GDT_Float32) && eBandType == GDT_Float64) )
1810 {
1811 bFilterOutStatsMetadata = true;
1812 }
1813 }
1814 }
1815
1816 /* -------------------------------------------------------------------- */
1817 /* Create this band. */
1818 /* -------------------------------------------------------------------- */
1819 CPLStringList aosAddBandOptions;
1820 if( bSpatialArrangementPreserved )
1821 {
1822 int nSrcBlockXSize, nSrcBlockYSize;
1823 poSrcBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
1824 aosAddBandOptions.SetNameValue("BLOCKXSIZE", CPLSPrintf("%d", nSrcBlockXSize));
1825 aosAddBandOptions.SetNameValue("BLOCKYSIZE", CPLSPrintf("%d", nSrcBlockYSize));
1826 }
1827 poVDS->AddBand( eBandType, aosAddBandOptions.List() );
1828 VRTSourcedRasterBand *poVRTBand =
1829 static_cast<VRTSourcedRasterBand *>(poVDS->GetRasterBand( i+1 ));
1830 if (nSrcBand < 0)
1831 {
1832 poVRTBand->AddMaskBandSource(poSrcBand,
1833 psOptions->adfSrcWin[0], psOptions->adfSrcWin[1],
1834 psOptions->adfSrcWin[2], psOptions->adfSrcWin[3],
1835 adfDstWin[0], adfDstWin[1],
1836 adfDstWin[2], adfDstWin[3]);
1837 continue;
1838 }
1839
1840 // Preserve NBITS if no option change values
1841 const char* pszNBits = poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1842 if( pszNBits && psOptions->nRGBExpand == 0 && psOptions->nScaleRepeat == 0 &&
1843 !psOptions->bUnscale && psOptions->eOutputType == GDT_Unknown && psOptions->pszResampling == nullptr )
1844 {
1845 poVRTBand->SetMetadataItem("NBITS", pszNBits, "IMAGE_STRUCTURE");
1846 }
1847
1848 // Preserve PIXELTYPE if no option change values
1849 const char* pszPixelType = poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
1850 if( pszPixelType && psOptions->nRGBExpand == 0 && psOptions->nScaleRepeat == 0 &&
1851 !psOptions->bUnscale && psOptions->eOutputType == GDT_Unknown && psOptions->pszResampling == nullptr )
1852 {
1853 poVRTBand->SetMetadataItem("PIXELTYPE", pszPixelType, "IMAGE_STRUCTURE");
1854 }
1855
1856 const char* pszCompression = poSrcBand->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
1857 if( pszCompression )
1858 {
1859 poVRTBand->SetMetadataItem("COMPRESSION", pszCompression, "IMAGE_STRUCTURE");
1860 }
1861
1862 /* -------------------------------------------------------------------- */
1863 /* Do we need to collect scaling information? */
1864 /* -------------------------------------------------------------------- */
1865 double dfScale = 1.0;
1866 double dfOffset = 0.0;
1867 int bScale = FALSE;
1868 bool bHaveScaleSrc = false;
1869 double dfScaleSrcMin = 0.0;
1870 double dfScaleSrcMax = 0.0;
1871 double dfScaleDstMin = 0.0;
1872 double dfScaleDstMax = 0.0;
1873 bool bExponentScaling = false;
1874 double dfExponent = 0.0;
1875
1876 // TODO(schwehr): Is bScale a bool?
1877 if( i < psOptions->nScaleRepeat && psOptions->pasScaleParams[i].bScale )
1878 {
1879 bScale = psOptions->pasScaleParams[i].bScale;
1880 bHaveScaleSrc = psOptions->pasScaleParams[i].bHaveScaleSrc;
1881 dfScaleSrcMin = psOptions->pasScaleParams[i].dfScaleSrcMin;
1882 dfScaleSrcMax = psOptions->pasScaleParams[i].dfScaleSrcMax;
1883 dfScaleDstMin = psOptions->pasScaleParams[i].dfScaleDstMin;
1884 dfScaleDstMax = psOptions->pasScaleParams[i].dfScaleDstMax;
1885 }
1886 else if( psOptions->nScaleRepeat == 1 && !psOptions->bHasUsedExplicitScaleBand )
1887 {
1888 bScale = psOptions->pasScaleParams[0].bScale;
1889 bHaveScaleSrc = psOptions->pasScaleParams[0].bHaveScaleSrc;
1890 dfScaleSrcMin = psOptions->pasScaleParams[0].dfScaleSrcMin;
1891 dfScaleSrcMax = psOptions->pasScaleParams[0].dfScaleSrcMax;
1892 dfScaleDstMin = psOptions->pasScaleParams[0].dfScaleDstMin;
1893 dfScaleDstMax = psOptions->pasScaleParams[0].dfScaleDstMax;
1894 }
1895
1896 if( i < psOptions->nExponentRepeat && psOptions->padfExponent[i] != 0.0 )
1897 {
1898 bExponentScaling = TRUE;
1899 dfExponent = psOptions->padfExponent[i];
1900 }
1901 else if( psOptions->nExponentRepeat == 1 && !psOptions->bHasUsedExplicitExponentBand )
1902 {
1903 bExponentScaling = TRUE;
1904 dfExponent = psOptions->padfExponent[0];
1905 }
1906
1907 if( bExponentScaling && !bScale )
1908 {
1909 CPLError( CE_Failure, CPLE_IllegalArg, "For band %d, -scale should be specified when -exponent is specified.", i + 1);
1910 if(pbUsageError)
1911 *pbUsageError = TRUE;
1912 GDALTranslateOptionsFree(psOptions);
1913 delete poVDS;
1914 return nullptr;
1915 }
1916
1917 if( bScale && !bHaveScaleSrc )
1918 {
1919 double adfCMinMax[2] = {};
1920 GDALComputeRasterMinMax( poSrcBand, TRUE, adfCMinMax );
1921 dfScaleSrcMin = adfCMinMax[0];
1922 dfScaleSrcMax = adfCMinMax[1];
1923 }
1924
1925 if( bScale )
1926 {
1927 /* To avoid a divide by zero */
1928 if( dfScaleSrcMax == dfScaleSrcMin )
1929 dfScaleSrcMax += 0.1;
1930
1931 // Can still occur for very big values
1932 if( dfScaleSrcMax == dfScaleSrcMin )
1933 {
1934 CPLError( CE_Failure, CPLE_AppDefined,
1935 "-scale cannot be applied due to source "
1936 "minimum and maximum being equal" );
1937 GDALTranslateOptionsFree(psOptions);
1938 delete poVDS;
1939 return nullptr;
1940 }
1941
1942 if( !bExponentScaling )
1943 {
1944 dfScale = (dfScaleDstMax - dfScaleDstMin)
1945 / (dfScaleSrcMax - dfScaleSrcMin);
1946 dfOffset = -1 * dfScaleSrcMin * dfScale + dfScaleDstMin;
1947 }
1948 }
1949
1950 if( psOptions->bUnscale )
1951 {
1952 dfScale = poSrcBand->GetScale();
1953 dfOffset = poSrcBand->GetOffset();
1954 }
1955
1956 /* -------------------------------------------------------------------- */
1957 /* Create a simple or complex data source depending on the */
1958 /* translation type required. */
1959 /* -------------------------------------------------------------------- */
1960 VRTSimpleSource* poSimpleSource = nullptr;
1961 if( psOptions->bUnscale || bScale || (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand) )
1962 {
1963 VRTComplexSource* poSource = new VRTComplexSource();
1964
1965 /* -------------------------------------------------------------------- */
1966 /* Set complex parameters. */
1967 /* -------------------------------------------------------------------- */
1968
1969 if( dfOffset != 0.0 || dfScale != 1.0 )
1970 {
1971 poSource->SetLinearScaling(dfOffset, dfScale);
1972 }
1973 else if( bExponentScaling )
1974 {
1975 poSource->SetPowerScaling(dfExponent,
1976 dfScaleSrcMin,
1977 dfScaleSrcMax,
1978 dfScaleDstMin,
1979 dfScaleDstMax);
1980 }
1981
1982 poSource->SetColorTableComponent(nComponent);
1983
1984 int bSuccess;
1985 double dfNoData = poSrcBand->GetNoDataValue( &bSuccess );
1986 if ( bSuccess )
1987 {
1988 poSource->SetNoDataValue(dfNoData);
1989 }
1990
1991 poSimpleSource = poSource;
1992 }
1993 else
1994 {
1995 poSimpleSource = new VRTSimpleSource();
1996 }
1997
1998 poSimpleSource->SetResampling(psOptions->pszResampling);
1999 poVRTBand->ConfigureSource( poSimpleSource,
2000 poSrcBand,
2001 FALSE,
2002 psOptions->adfSrcWin[0], psOptions->adfSrcWin[1],
2003 psOptions->adfSrcWin[2], psOptions->adfSrcWin[3],
2004 adfDstWin[0], adfDstWin[1],
2005 adfDstWin[2], adfDstWin[3] );
2006
2007 poVRTBand->AddSource( poSimpleSource );
2008
2009 /* -------------------------------------------------------------------- */
2010 /* In case of color table translate, we only set the color */
2011 /* interpretation other info copied by CopyBandInfo are */
2012 /* not relevant in RGB expansion. */
2013 /* -------------------------------------------------------------------- */
2014 if (psOptions->nRGBExpand == 1)
2015 {
2016 poVRTBand->SetColorInterpretation( GCI_GrayIndex );
2017 }
2018 else if (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand)
2019 {
2020 poVRTBand->SetColorInterpretation( static_cast<GDALColorInterp>(GCI_RedBand + i) );
2021 }
2022
2023 /* -------------------------------------------------------------------- */
2024 /* copy over some other information of interest. */
2025 /* -------------------------------------------------------------------- */
2026 else
2027 {
2028 CopyBandInfo( poSrcBand, poVRTBand,
2029 !psOptions->bStats && !bFilterOutStatsMetadata,
2030 !psOptions->bUnscale && !psOptions->bSetScale &&
2031 !psOptions->bSetOffset,
2032 !psOptions->bSetNoData && !psOptions->bUnsetNoData,
2033 !psOptions->bNoRAT,
2034 psOptions );
2035 if( psOptions->nScaleRepeat == 0 &&
2036 psOptions->nExponentRepeat == 0 &&
2037 EQUAL(psOptions->pszFormat, "GRIB") )
2038 {
2039 char** papszMD_GRIB = poSrcBand->GetMetadata("GRIB");
2040 if( papszMD_GRIB != nullptr)
2041 poVRTBand->SetMetadata( papszMD_GRIB, "GRIB" );
2042 }
2043 }
2044
2045 // Color interpretation override
2046 if( psOptions->panColorInterp )
2047 {
2048 if( i < psOptions->nColorInterpSize &&
2049 psOptions->panColorInterp[i] >= 0 )
2050 {
2051 poVRTBand->SetColorInterpretation(
2052 static_cast<GDALColorInterp>(psOptions->panColorInterp[i]));
2053 }
2054 }
2055
2056 /* -------------------------------------------------------------------- */
2057 /* Set a forcible nodata value? */
2058 /* -------------------------------------------------------------------- */
2059 if( psOptions->bSetNoData )
2060 {
2061 const double dfVal = AdjustNoDataValue(
2062 psOptions->dfNoDataReal, poVRTBand, psOptions);
2063 poVRTBand->SetNoDataValue( dfVal );
2064 }
2065
2066 if( psOptions->bSetScale )
2067 poVRTBand->SetScale( psOptions->dfScale );
2068
2069 if( psOptions->bSetOffset )
2070 poVRTBand->SetOffset( psOptions->dfOffset );
2071
2072 if (psOptions->eMaskMode == MASK_AUTO &&
2073 (GDALGetMaskFlags(GDALGetRasterBand(hSrcDataset, 1)) & GMF_PER_DATASET) == 0 &&
2074 (poSrcBand->GetMaskFlags() & (GMF_ALL_VALID | GMF_NODATA)) == 0)
2075 {
2076 if (poVRTBand->CreateMaskBand(poSrcBand->GetMaskFlags()) == CE_None)
2077 {
2078 VRTSourcedRasterBand* hMaskVRTBand =
2079 cpl::down_cast<VRTSourcedRasterBand*>(poVRTBand->GetMaskBand());
2080 hMaskVRTBand->AddMaskBandSource(poSrcBand,
2081 psOptions->adfSrcWin[0], psOptions->adfSrcWin[1],
2082 psOptions->adfSrcWin[2], psOptions->adfSrcWin[3],
2083 adfDstWin[0], adfDstWin[1],
2084 adfDstWin[2], adfDstWin[3] );
2085 }
2086 }
2087 }
2088
2089 if (psOptions->eMaskMode == MASK_USER)
2090 {
2091 GDALRasterBand *poSrcBand =
2092 static_cast<GDALRasterBand*>(GDALGetRasterBand(hSrcDataset,
2093 std::abs(psOptions->nMaskBand)));
2094 if (poSrcBand && poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
2095 {
2096 VRTSourcedRasterBand* hMaskVRTBand = static_cast<VRTSourcedRasterBand*>(
2097 GDALGetMaskBand(GDALGetRasterBand(static_cast<GDALDataset*>(poVDS), 1)));
2098 if (psOptions->nMaskBand > 0)
2099 hMaskVRTBand->AddSimpleSource(poSrcBand,
2100 psOptions->adfSrcWin[0], psOptions->adfSrcWin[1],
2101 psOptions->adfSrcWin[2], psOptions->adfSrcWin[3],
2102 adfDstWin[0], adfDstWin[1],
2103 adfDstWin[2], adfDstWin[3] );
2104 else
2105 hMaskVRTBand->AddMaskBandSource(poSrcBand,
2106 psOptions->adfSrcWin[0], psOptions->adfSrcWin[1],
2107 psOptions->adfSrcWin[2], psOptions->adfSrcWin[3],
2108 adfDstWin[0], adfDstWin[1],
2109 adfDstWin[2], adfDstWin[3] );
2110 }
2111 }
2112 else
2113 if (psOptions->eMaskMode == MASK_AUTO && nSrcBandCount > 0 &&
2114 GDALGetMaskFlags(GDALGetRasterBand(hSrcDataset, 1)) == GMF_PER_DATASET)
2115 {
2116 if (poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
2117 {
2118 VRTSourcedRasterBand* hMaskVRTBand = static_cast<VRTSourcedRasterBand*>(
2119 GDALGetMaskBand(GDALGetRasterBand(static_cast<GDALDataset*>(poVDS), 1)));
2120 hMaskVRTBand->AddMaskBandSource(static_cast<GDALRasterBand*>(GDALGetRasterBand(hSrcDataset, 1)),
2121 psOptions->adfSrcWin[0], psOptions->adfSrcWin[1],
2122 psOptions->adfSrcWin[2], psOptions->adfSrcWin[3],
2123 adfDstWin[0], adfDstWin[1],
2124 adfDstWin[2], adfDstWin[3] );
2125 }
2126 }
2127
2128 /* -------------------------------------------------------------------- */
2129 /* Compute stats if required. */
2130 /* -------------------------------------------------------------------- */
2131 if (psOptions->bStats)
2132 {
2133 for( int i = 0; i < poVDS->GetRasterCount(); i++ )
2134 {
2135 double dfMin, dfMax, dfMean, dfStdDev;
2136 poVDS->GetRasterBand(i+1)->ComputeStatistics( psOptions->bApproxStats,
2137 &dfMin, &dfMax, &dfMean, &dfStdDev, GDALDummyProgress, nullptr );
2138 }
2139 }
2140
2141 /* -------------------------------------------------------------------- */
2142 /* Write to the output file using CopyCreate(). */
2143 /* -------------------------------------------------------------------- */
2144 if( EQUAL(psOptions->pszFormat, "VRT") &&
2145 psOptions->papszCreateOptions == nullptr )
2146 {
2147 poVDS->SetDescription(pszDest);
2148 hOutDS = static_cast<GDALDatasetH>(poVDS);
2149 if( !EQUAL(pszDest, "") )
2150 {
2151 hOutDS = GDALTranslateFlush(hOutDS);
2152 }
2153 }
2154 else
2155 {
2156 hOutDS = GDALCreateCopy( hDriver, pszDest, static_cast<GDALDatasetH>(poVDS),
2157 psOptions->bStrict, psOptions->papszCreateOptions,
2158 psOptions->pfnProgress, psOptions->pProgressData );
2159 hOutDS = GDALTranslateFlush(hOutDS);
2160
2161 GDALClose(poVDS);
2162 }
2163
2164 GDALTranslateOptionsFree(psOptions);
2165 return hOutDS;
2166 }
2167
2168 /************************************************************************/
2169 /* AttachMetadata() */
2170 /************************************************************************/
2171
AttachMetadata(GDALDatasetH hDS,char ** papszMetadataOptions)2172 static void AttachMetadata( GDALDatasetH hDS, char **papszMetadataOptions )
2173
2174 {
2175 const int nCount = CSLCount(papszMetadataOptions);
2176
2177 for( int i = 0; i < nCount; i++ )
2178 {
2179 char *pszKey = nullptr;
2180 const char *pszValue =
2181 CPLParseNameValue(papszMetadataOptions[i], &pszKey);
2182 if( pszKey && pszValue )
2183 {
2184 GDALSetMetadataItem(hDS,pszKey,pszValue,nullptr);
2185 }
2186 CPLFree( pszKey );
2187 }
2188 }
2189
2190 /************************************************************************/
2191 /* CopyBandInfo() */
2192 /************************************************************************/
2193
2194 /* A bit of a clone of VRTRasterBand::CopyCommonInfoFrom(), but we need */
2195 /* more and more custom behavior in the context of gdal_translate ... */
2196
CopyBandInfo(GDALRasterBand * poSrcBand,GDALRasterBand * poDstBand,int bCanCopyStatsMetadata,int bCopyScale,int bCopyNoData,bool bCopyRAT,const GDALTranslateOptions * psOptions)2197 static void CopyBandInfo( GDALRasterBand * poSrcBand, GDALRasterBand * poDstBand,
2198 int bCanCopyStatsMetadata, int bCopyScale, int bCopyNoData, bool bCopyRAT,
2199 const GDALTranslateOptions *psOptions )
2200
2201 {
2202
2203 if (bCanCopyStatsMetadata)
2204 {
2205 poDstBand->SetMetadata( poSrcBand->GetMetadata() );
2206 if (bCopyRAT)
2207 {
2208 poDstBand->SetDefaultRAT( poSrcBand->GetDefaultRAT() );
2209 }
2210 }
2211 else
2212 {
2213 char** papszMetadata = poSrcBand->GetMetadata();
2214 char** papszMetadataNew = nullptr;
2215 for( int i = 0; papszMetadata != nullptr && papszMetadata[i] != nullptr; i++ )
2216 {
2217 if (!STARTS_WITH(papszMetadata[i], "STATISTICS_"))
2218 papszMetadataNew = CSLAddString(papszMetadataNew, papszMetadata[i]);
2219 }
2220 poDstBand->SetMetadata( papszMetadataNew );
2221 CSLDestroy(papszMetadataNew);
2222
2223 // we need to strip histogram data from the source RAT
2224 if (poSrcBand->GetDefaultRAT() && bCopyRAT)
2225 {
2226 GDALRasterAttributeTable *poNewRAT = poSrcBand->GetDefaultRAT()->Clone();
2227
2228 // strip histogram data (as defined by the source RAT)
2229 poNewRAT->RemoveStatistics();
2230 if( poNewRAT->GetColumnCount() )
2231 {
2232 poDstBand->SetDefaultRAT( poNewRAT );
2233 }
2234 // since SetDefaultRAT copies the RAT data we need to delete our original
2235 delete poNewRAT;
2236 }
2237 }
2238
2239 poDstBand->SetColorTable( poSrcBand->GetColorTable() );
2240 poDstBand->SetColorInterpretation(poSrcBand->GetColorInterpretation());
2241 if( strlen(poSrcBand->GetDescription()) > 0 )
2242 poDstBand->SetDescription( poSrcBand->GetDescription() );
2243
2244 if (bCopyNoData)
2245 {
2246 int bSuccess = FALSE;
2247 double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
2248 if( bSuccess )
2249 {
2250 const double dfVal = AdjustNoDataValue(
2251 dfNoData, poDstBand, psOptions);
2252 poDstBand->SetNoDataValue( dfVal );
2253 }
2254 }
2255
2256 if (bCopyScale)
2257 {
2258 poDstBand->SetOffset( poSrcBand->GetOffset() );
2259 poDstBand->SetScale( poSrcBand->GetScale() );
2260 }
2261
2262 poDstBand->SetCategoryNames( poSrcBand->GetCategoryNames() );
2263
2264 // Copy unit only if the range of pixel values is not modified
2265 if( bCanCopyStatsMetadata && bCopyScale && !EQUAL(poSrcBand->GetUnitType(),"") )
2266 poDstBand->SetUnitType( poSrcBand->GetUnitType() );
2267 }
2268
2269 /************************************************************************/
2270 /* ArgIsNumeric() */
2271 /************************************************************************/
2272
ArgIsNumeric(const char * pszArg)2273 int ArgIsNumeric( const char *pszArg )
2274
2275 {
2276 return CPLGetValueType(pszArg) != CPL_VALUE_STRING;
2277 }
2278
2279 /************************************************************************/
2280 /* GetColorInterp() */
2281 /************************************************************************/
2282
GetColorInterp(const char * pszStr)2283 static int GetColorInterp( const char* pszStr )
2284 {
2285 if( EQUAL(pszStr, "red") )
2286 return GCI_RedBand;
2287 if( EQUAL(pszStr, "green") )
2288 return GCI_GreenBand;
2289 if( EQUAL(pszStr, "blue") )
2290 return GCI_BlueBand;
2291 if( EQUAL(pszStr, "alpha") )
2292 return GCI_AlphaBand;
2293 if( EQUAL(pszStr, "gray") || EQUAL(pszStr, "grey") )
2294 return GCI_GrayIndex;
2295 if( EQUAL(pszStr, "undefined") )
2296 return GCI_Undefined;
2297 CPLError(CE_Warning, CPLE_NotSupported,
2298 "Unsupported color interpretation: %s", pszStr);
2299 return -1;
2300 }
2301
2302 /************************************************************************/
2303 /* GDALTranslateOptionsNew() */
2304 /************************************************************************/
2305
2306 /**
2307 * Allocates a GDALTranslateOptions struct.
2308 *
2309 * @param papszArgv NULL terminated list of options (potentially including filename and open options too), or NULL.
2310 * The accepted options are the ones of the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
2311 * @param psOptionsForBinary (output) may be NULL (and should generally be NULL),
2312 * otherwise (gdal_translate_bin.cpp use case) must be allocated with
2313 * GDALTranslateOptionsForBinaryNew() prior to this function. Will be
2314 * filled with potentially present filename, open options,...
2315 * @return pointer to the allocated GDALTranslateOptions struct. Must be freed with GDALTranslateOptionsFree().
2316 *
2317 * @since GDAL 2.1
2318 */
2319
GDALTranslateOptionsNew(char ** papszArgv,GDALTranslateOptionsForBinary * psOptionsForBinary)2320 GDALTranslateOptions *GDALTranslateOptionsNew(char** papszArgv, GDALTranslateOptionsForBinary* psOptionsForBinary)
2321 {
2322 GDALTranslateOptions *psOptions = static_cast<GDALTranslateOptions *>(
2323 CPLCalloc( 1, sizeof(GDALTranslateOptions)));
2324
2325 psOptions->pszFormat = nullptr;
2326 psOptions->bQuiet = true;
2327 psOptions->pfnProgress = GDALDummyProgress;
2328 psOptions->pProgressData = nullptr;
2329 psOptions->eOutputType = GDT_Unknown;
2330 psOptions->eMaskMode = MASK_AUTO;
2331 psOptions->nBandCount = 0;
2332 psOptions->panBandList = nullptr;
2333 psOptions->nOXSizePixel = 0;
2334 psOptions->nOYSizePixel = 0;
2335 psOptions->dfOXSizePct = 0.0;
2336 psOptions->dfOYSizePct = 0.0;
2337 psOptions->adfSrcWin[0] = 0;
2338 psOptions->adfSrcWin[1] = 0;
2339 psOptions->adfSrcWin[2] = 0;
2340 psOptions->adfSrcWin[3] = 0;
2341 psOptions->bStrict = false;
2342 psOptions->bUnscale = false;
2343 psOptions->bSetScale = false;
2344 psOptions->dfScale = 1.0;
2345 psOptions->bSetOffset = false;
2346 psOptions->dfOffset = 0.0;
2347 psOptions->nScaleRepeat = 0;
2348 psOptions->pasScaleParams = nullptr;
2349 psOptions->bHasUsedExplicitScaleBand = false;
2350 psOptions->nExponentRepeat = 0;
2351 psOptions->padfExponent = nullptr;
2352 psOptions->bHasUsedExplicitExponentBand = false;
2353 psOptions->dfULX = 0.0;
2354 psOptions->dfULY = 0.0;
2355 psOptions->dfLRX = 0.0;
2356 psOptions->dfLRY = 0.0;
2357 psOptions->pszOutputSRS = nullptr;
2358 psOptions->bNoGCP = false;
2359 psOptions->nGCPCount = 0;
2360 psOptions->pasGCPs = nullptr;
2361 psOptions->adfULLR[0] = 0;
2362 psOptions->adfULLR[1] = 0;
2363 psOptions->adfULLR[2] = 0;
2364 psOptions->adfULLR[3] = 0;
2365 psOptions->bSetNoData = false;
2366 psOptions->bUnsetNoData = false;
2367 psOptions->dfNoDataReal = 0.0;
2368 psOptions->nRGBExpand = 0;
2369 psOptions->nMaskBand = 0;
2370 psOptions->bStats = false;
2371 psOptions->bApproxStats = false;
2372 psOptions->bErrorOnPartiallyOutside = false;
2373 psOptions->bErrorOnCompletelyOutside = false;
2374 psOptions->bNoRAT = false;
2375 psOptions->pszResampling = nullptr;
2376 psOptions->dfXRes = 0.0;
2377 psOptions->dfYRes = 0.0;
2378 psOptions->pszProjSRS = nullptr;
2379 psOptions->nLimitOutSize = 0;
2380 psOptions->bNoXMP = false;
2381
2382 bool bParsedMaskArgument = false;
2383 bool bOutsideExplicitlySet = false;
2384 bool bGotSourceFilename = false;
2385 bool bGotDestFilename = false;
2386
2387 /* -------------------------------------------------------------------- */
2388 /* Handle command line arguments. */
2389 /* -------------------------------------------------------------------- */
2390 const int argc = CSLCount(papszArgv);
2391 for( int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr; i++ )
2392 {
2393 if( i < argc-1 && (EQUAL(papszArgv[i],"-of") || EQUAL(papszArgv[i],"-f")) )
2394 {
2395 ++i;
2396 CPLFree(psOptions->pszFormat);
2397 psOptions->pszFormat = CPLStrdup(papszArgv[i]);
2398 }
2399
2400 else if( EQUAL(papszArgv[i],"-q") || EQUAL(papszArgv[i],"-quiet") )
2401 {
2402 if( psOptionsForBinary )
2403 psOptionsForBinary->bQuiet = true;
2404 }
2405
2406 else if( EQUAL(papszArgv[i],"-ot") && papszArgv[i+1] )
2407 {
2408 for( int iType = 1; iType < GDT_TypeCount; iType++ )
2409 {
2410 if( GDALGetDataTypeName(static_cast<GDALDataType>(iType)) != nullptr
2411 && EQUAL(GDALGetDataTypeName(static_cast<GDALDataType>(iType)),
2412 papszArgv[i+1]) )
2413 {
2414 psOptions->eOutputType = static_cast<GDALDataType>(iType);
2415 }
2416 }
2417
2418 if( psOptions->eOutputType == GDT_Unknown )
2419 {
2420 CPLError(CE_Failure, CPLE_NotSupported,
2421 "Unknown output pixel type: %s.", papszArgv[i+1] );
2422 GDALTranslateOptionsFree(psOptions);
2423 return nullptr;
2424 }
2425 i++;
2426 }
2427 else if( EQUAL(papszArgv[i],"-b") && papszArgv[i+1] )
2428 {
2429 const char* pszBand = papszArgv[i+1];
2430 bool bMask = false;
2431 if (EQUAL(pszBand, "mask"))
2432 pszBand = "mask,1";
2433 if (STARTS_WITH_CI(pszBand, "mask,"))
2434 {
2435 bMask = true;
2436 pszBand += 5;
2437 /* If we use the source mask band as a regular band */
2438 /* don't create a target mask band by default */
2439 if( !bParsedMaskArgument )
2440 psOptions->eMaskMode = MASK_DISABLED;
2441 }
2442 const int nBand = atoi(pszBand);
2443 if( nBand < 1 )
2444 {
2445 CPLError(CE_Failure, CPLE_NotSupported,"Unrecognizable band number (%s).", papszArgv[i+1] );
2446 GDALTranslateOptionsFree(psOptions);
2447 return nullptr;
2448 }
2449 i++;
2450
2451 psOptions->nBandCount++;
2452 psOptions->panBandList = static_cast<int *>(
2453 CPLRealloc(psOptions->panBandList,
2454 sizeof(int) * psOptions->nBandCount));
2455 psOptions->panBandList[psOptions->nBandCount-1] = nBand;
2456 if (bMask)
2457 psOptions->panBandList[psOptions->nBandCount-1] *= -1;
2458 }
2459 else if( EQUAL(papszArgv[i],"-mask") && papszArgv[i+1] )
2460 {
2461 bParsedMaskArgument = true;
2462 const char* pszBand = papszArgv[i+1];
2463 if (EQUAL(pszBand, "none"))
2464 {
2465 psOptions->eMaskMode = MASK_DISABLED;
2466 }
2467 else if (EQUAL(pszBand, "auto"))
2468 {
2469 psOptions->eMaskMode = MASK_AUTO;
2470 }
2471 else
2472 {
2473 bool bMask = false;
2474 if (EQUAL(pszBand, "mask"))
2475 pszBand = "mask,1";
2476 if (STARTS_WITH_CI(pszBand, "mask,"))
2477 {
2478 bMask = true;
2479 pszBand += 5;
2480 }
2481 const int nBand = atoi(pszBand);
2482 if( nBand < 1 )
2483 {
2484 CPLError(CE_Failure, CPLE_NotSupported,"Unrecognizable band number (%s).", papszArgv[i+1] );
2485 GDALTranslateOptionsFree(psOptions);
2486 return nullptr;
2487 }
2488
2489 psOptions->eMaskMode = MASK_USER;
2490 psOptions->nMaskBand = nBand;
2491 if (bMask)
2492 psOptions->nMaskBand *= -1;
2493 }
2494 i ++;
2495 }
2496 else if( EQUAL(papszArgv[i],"-not_strict") )
2497 {
2498 psOptions->bStrict = false;
2499
2500 }
2501 else if( EQUAL(papszArgv[i],"-strict") )
2502 {
2503 psOptions->bStrict = true;
2504 }
2505 else if( EQUAL(papszArgv[i],"-sds") )
2506 {
2507 if( psOptionsForBinary )
2508 psOptionsForBinary->bCopySubDatasets = TRUE;
2509 }
2510 else if (EQUAL(papszArgv[i], "-nogcp"))
2511 {
2512 psOptions->bNoGCP = true;
2513 }
2514 else if( i + 4 < argc && EQUAL(papszArgv[i],"-gcp") )
2515 {
2516 /* -gcp pixel line easting northing [elev] */
2517 psOptions->nGCPCount++;
2518 psOptions->pasGCPs = static_cast<GDAL_GCP *>(
2519 CPLRealloc(psOptions->pasGCPs,
2520 sizeof(GDAL_GCP) * psOptions->nGCPCount));
2521 GDALInitGCPs( 1, psOptions->pasGCPs + psOptions->nGCPCount - 1 );
2522
2523 psOptions->pasGCPs[psOptions->nGCPCount-1].dfGCPPixel = CPLAtofM(papszArgv[++i]);
2524 psOptions->pasGCPs[psOptions->nGCPCount-1].dfGCPLine = CPLAtofM(papszArgv[++i]);
2525 psOptions->pasGCPs[psOptions->nGCPCount-1].dfGCPX = CPLAtofM(papszArgv[++i]);
2526 psOptions->pasGCPs[psOptions->nGCPCount-1].dfGCPY = CPLAtofM(papszArgv[++i]);
2527
2528 char* endptr = nullptr;
2529 if( papszArgv[i+1] != nullptr
2530 && (CPLStrtod(papszArgv[i+1], &endptr) != 0.0 || papszArgv[i+1][0] == '0') )
2531 {
2532 /* Check that last argument is really a number and not a filename */
2533 /* looking like a number (see ticket #863) */
2534 if (endptr && *endptr == 0)
2535 psOptions->pasGCPs[psOptions->nGCPCount-1].dfGCPZ = CPLAtofM(papszArgv[++i]);
2536 }
2537
2538 /* should set id and info? */
2539 }
2540
2541 else if( EQUAL(papszArgv[i],"-a_nodata") && papszArgv[i+1] )
2542 {
2543 if (EQUAL(papszArgv[i+1], "none"))
2544 {
2545 psOptions->bUnsetNoData = true;
2546 }
2547 else
2548 {
2549 psOptions->bSetNoData = true;
2550 psOptions->dfNoDataReal = CPLAtofM(papszArgv[i+1]);
2551 }
2552 i += 1;
2553 }
2554
2555 else if( EQUAL(papszArgv[i],"-a_scale") && papszArgv[i+1] )
2556 {
2557 psOptions->bSetScale = true;
2558 psOptions->dfScale = CPLAtofM(papszArgv[i+1]);
2559 i += 1;
2560 }
2561
2562 else if( EQUAL(papszArgv[i],"-a_offset") && papszArgv[i+1] )
2563 {
2564 psOptions->bSetOffset = true;
2565 psOptions->dfOffset = CPLAtofM(papszArgv[i+1]);
2566 i += 1;
2567 }
2568
2569
2570 else if( i + 4 < argc && EQUAL(papszArgv[i],"-a_ullr") )
2571 {
2572 psOptions->adfULLR[0] = CPLAtofM(papszArgv[i+1]);
2573 psOptions->adfULLR[1] = CPLAtofM(papszArgv[i+2]);
2574 psOptions->adfULLR[2] = CPLAtofM(papszArgv[i+3]);
2575 psOptions->adfULLR[3] = CPLAtofM(papszArgv[i+4]);
2576
2577 i += 4;
2578 }
2579
2580 else if( EQUAL(papszArgv[i],"-co") && papszArgv[i+1] )
2581 {
2582 psOptions->papszCreateOptions = CSLAddString( psOptions->papszCreateOptions, papszArgv[++i] );
2583 }
2584
2585 else if( EQUAL(papszArgv[i],"-scale") || STARTS_WITH_CI(papszArgv[i], "-scale_") )
2586 {
2587 int nIndex = 0;
2588 if( STARTS_WITH_CI(papszArgv[i], "-scale_") )
2589 {
2590 if( !psOptions->bHasUsedExplicitScaleBand && psOptions->nScaleRepeat != 0 )
2591 {
2592 CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -scale and -scale_XX syntax");
2593 GDALTranslateOptionsFree(psOptions);
2594 return nullptr;
2595 }
2596 psOptions->bHasUsedExplicitScaleBand = true;
2597 nIndex = atoi(papszArgv[i] + 7);
2598 if( nIndex <= 0 || nIndex > 65535 )
2599 {
2600 CPLError(CE_Failure, CPLE_NotSupported, "Invalid parameter name: %s", papszArgv[i]);
2601 GDALTranslateOptionsFree(psOptions);
2602 return nullptr;
2603 }
2604 nIndex --;
2605 }
2606 else
2607 {
2608 if( psOptions->bHasUsedExplicitScaleBand )
2609 {
2610 CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -scale and -scale_XX syntax");
2611 GDALTranslateOptionsFree(psOptions);
2612 return nullptr;
2613 }
2614 nIndex = psOptions->nScaleRepeat;
2615 }
2616
2617 if( nIndex >= psOptions->nScaleRepeat )
2618 {
2619 psOptions->pasScaleParams =
2620 static_cast<GDALTranslateScaleParams*>(
2621 CPLRealloc(psOptions->pasScaleParams,
2622 (nIndex + 1) *
2623 sizeof(GDALTranslateScaleParams)));
2624 memset(psOptions->pasScaleParams + psOptions->nScaleRepeat, 0,
2625 sizeof(GDALTranslateScaleParams) * (nIndex - psOptions->nScaleRepeat + 1));
2626 psOptions->nScaleRepeat = nIndex + 1;
2627 }
2628 psOptions->pasScaleParams[nIndex].bScale = TRUE;
2629 psOptions->pasScaleParams[nIndex].bHaveScaleSrc = false;
2630 if( i < argc-2 && ArgIsNumeric(papszArgv[i+1]) )
2631 {
2632 psOptions->pasScaleParams[nIndex].bHaveScaleSrc = true;
2633 psOptions->pasScaleParams[nIndex].dfScaleSrcMin = CPLAtofM(papszArgv[i+1]);
2634 psOptions->pasScaleParams[nIndex].dfScaleSrcMax = CPLAtofM(papszArgv[i+2]);
2635 i += 2;
2636 }
2637 if( i < argc-2 && psOptions->pasScaleParams[nIndex].bHaveScaleSrc && ArgIsNumeric(papszArgv[i+1]) )
2638 {
2639 psOptions->pasScaleParams[nIndex].dfScaleDstMin = CPLAtofM(papszArgv[i+1]);
2640 psOptions->pasScaleParams[nIndex].dfScaleDstMax = CPLAtofM(papszArgv[i+2]);
2641 i += 2;
2642 }
2643 else
2644 {
2645 psOptions->pasScaleParams[nIndex].dfScaleDstMin = 0.0;
2646 psOptions->pasScaleParams[nIndex].dfScaleDstMax = 255.999;
2647 }
2648 }
2649
2650 else if( (EQUAL(papszArgv[i],"-exponent") || STARTS_WITH_CI(papszArgv[i], "-exponent_")) &&
2651 papszArgv[i+1] )
2652 {
2653 int nIndex = 0;
2654 if( STARTS_WITH_CI(papszArgv[i], "-exponent_") )
2655 {
2656 if( !psOptions->bHasUsedExplicitExponentBand && psOptions->nExponentRepeat != 0 )
2657 {
2658 CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -exponent and -exponent_XX syntax");
2659 GDALTranslateOptionsFree(psOptions);
2660 return nullptr;
2661 }
2662 psOptions->bHasUsedExplicitExponentBand = true;
2663 nIndex = atoi(papszArgv[i] + 10);
2664 if( nIndex <= 0 || nIndex > 65535 )
2665 {
2666 CPLError(CE_Failure, CPLE_NotSupported, "Invalid parameter name: %s", papszArgv[i]);
2667 GDALTranslateOptionsFree(psOptions);
2668 return nullptr;
2669 }
2670 nIndex --;
2671 }
2672 else
2673 {
2674 if( psOptions->bHasUsedExplicitExponentBand )
2675 {
2676 CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -exponent and -exponent_XX syntax");
2677 GDALTranslateOptionsFree(psOptions);
2678 return nullptr;
2679 }
2680 nIndex = psOptions->nExponentRepeat;
2681 }
2682
2683 if( nIndex >= psOptions->nExponentRepeat )
2684 {
2685 psOptions->padfExponent = static_cast<double *>(
2686 CPLRealloc(psOptions->padfExponent,
2687 (nIndex + 1) * sizeof(double)));
2688 if( nIndex > psOptions->nExponentRepeat )
2689 memset(psOptions->padfExponent + psOptions->nExponentRepeat, 0,
2690 sizeof(double) * (nIndex - psOptions->nExponentRepeat));
2691 psOptions->nExponentRepeat = nIndex + 1;
2692 }
2693 double dfExponent = CPLAtofM(papszArgv[++i]);
2694 psOptions->padfExponent[nIndex] = dfExponent;
2695 }
2696
2697 else if( EQUAL(papszArgv[i], "-unscale") )
2698 {
2699 psOptions->bUnscale = true;
2700 }
2701
2702 else if( EQUAL(papszArgv[i],"-mo") && papszArgv[i+1] )
2703 {
2704 psOptions->papszMetadataOptions = CSLAddString( psOptions->papszMetadataOptions,
2705 papszArgv[++i] );
2706 }
2707
2708 else if( i+2 < argc && EQUAL(papszArgv[i],"-outsize") && papszArgv[i+1] != nullptr )
2709 {
2710 ++i;
2711 if( papszArgv[i][0] != '\0' &&
2712 papszArgv[i][strlen(papszArgv[i])-1] == '%' )
2713 psOptions->dfOXSizePct = CPLAtofM(papszArgv[i]);
2714 else
2715 psOptions->nOXSizePixel = atoi(papszArgv[i]);
2716 ++i;
2717 if( papszArgv[i][0] != '\0' &&
2718 papszArgv[i][strlen(papszArgv[i])-1] == '%' )
2719 psOptions->dfOYSizePct = CPLAtofM(papszArgv[i]);
2720 else
2721 psOptions->nOYSizePixel = atoi(papszArgv[i]);
2722 bOutsideExplicitlySet = true;
2723 }
2724
2725 else if( i+2 < argc && EQUAL(papszArgv[i],"-tr") )
2726 {
2727 psOptions->dfXRes = CPLAtofM(papszArgv[++i]);
2728 psOptions->dfYRes = fabs(CPLAtofM(papszArgv[++i]));
2729 if( psOptions->dfXRes == 0 || psOptions->dfYRes == 0 )
2730 {
2731 CPLError(CE_Failure, CPLE_IllegalArg, "Wrong value for -tr parameters.");
2732 GDALTranslateOptionsFree(psOptions);
2733 return nullptr;
2734 }
2735 }
2736
2737 else if( i+4 < argc && EQUAL(papszArgv[i],"-srcwin") )
2738 {
2739 psOptions->adfSrcWin[0] = CPLAtof(papszArgv[++i]);
2740 psOptions->adfSrcWin[1] = CPLAtof(papszArgv[++i]);
2741 psOptions->adfSrcWin[2] = CPLAtof(papszArgv[++i]);
2742 psOptions->adfSrcWin[3] = CPLAtof(papszArgv[++i]);
2743 }
2744
2745 else if( i+4 < argc && EQUAL(papszArgv[i],"-projwin") )
2746 {
2747 psOptions->dfULX = CPLAtofM(papszArgv[++i]);
2748 psOptions->dfULY = CPLAtofM(papszArgv[++i]);
2749 psOptions->dfLRX = CPLAtofM(papszArgv[++i]);
2750 psOptions->dfLRY = CPLAtofM(papszArgv[++i]);
2751 }
2752
2753 else if( i+1 < argc && EQUAL(papszArgv[i],"-projwin_srs") )
2754 {
2755 CPLFree(psOptions->pszProjSRS);
2756 psOptions->pszProjSRS = CPLStrdup(papszArgv[i+1]);
2757 i++;
2758 }
2759
2760 else if( EQUAL(papszArgv[i],"-epo") )
2761 {
2762 psOptions->bErrorOnPartiallyOutside = true;
2763 psOptions->bErrorOnCompletelyOutside = true;
2764 }
2765
2766 else if( EQUAL(papszArgv[i],"-eco") )
2767 {
2768 psOptions->bErrorOnCompletelyOutside = true;
2769 }
2770
2771 else if( i+1 < argc && EQUAL(papszArgv[i],"-a_srs") )
2772 {
2773 CPLFree(psOptions->pszOutputSRS);
2774 psOptions->pszOutputSRS = CPLStrdup(papszArgv[i+1]);
2775 i++;
2776 }
2777
2778 else if( i+1 < argc && EQUAL(papszArgv[i],"-expand") && papszArgv[i+1] != nullptr )
2779 {
2780 i++;
2781 if (EQUAL(papszArgv[i], "gray"))
2782 psOptions->nRGBExpand = 1;
2783 else if (EQUAL(papszArgv[i], "rgb"))
2784 psOptions->nRGBExpand = 3;
2785 else if (EQUAL(papszArgv[i], "rgba"))
2786 psOptions->nRGBExpand = 4;
2787 else
2788 {
2789 CPLError(CE_Failure, CPLE_IllegalArg,
2790 "Value %s unsupported. Only gray, rgb or rgba are supported.",
2791 papszArgv[i] );
2792 GDALTranslateOptionsFree(psOptions);
2793 return nullptr;
2794 }
2795 }
2796
2797 else if( EQUAL(papszArgv[i], "-stats") )
2798 {
2799 psOptions->bStats = true;
2800 psOptions->bApproxStats = false;
2801 }
2802 else if( EQUAL(papszArgv[i], "-approx_stats") )
2803 {
2804 psOptions->bStats = true;
2805 psOptions->bApproxStats = true;
2806 }
2807 else if( EQUAL(papszArgv[i], "-norat") )
2808 {
2809 psOptions->bNoRAT = true;
2810 }
2811 else if( i+1 < argc && EQUAL(papszArgv[i], "-oo") )
2812 {
2813 i++;
2814 if( psOptionsForBinary )
2815 {
2816 psOptionsForBinary->papszOpenOptions =
2817 CSLAddString( psOptionsForBinary->papszOpenOptions,
2818 papszArgv[i] );
2819 }
2820 }
2821 else if( i+1 < argc && EQUAL(papszArgv[i],"-r") )
2822 {
2823 CPLFree(psOptions->pszResampling);
2824 psOptions->pszResampling = CPLStrdup(papszArgv[++i]);
2825 }
2826
2827 else if( EQUAL(papszArgv[i],"-colorinterp") && papszArgv[i+1] )
2828 {
2829 ++i;
2830 CPLStringList aosList(CSLTokenizeString2(papszArgv[i], ",", 0));
2831 psOptions->nColorInterpSize = aosList.size();
2832 psOptions->panColorInterp = static_cast<int *>(
2833 CPLRealloc(psOptions->panColorInterp,
2834 psOptions->nColorInterpSize * sizeof(int)));
2835 for( int j = 0; j < aosList.size(); j++ )
2836 {
2837 psOptions->panColorInterp[j] = GetColorInterp(aosList[j]);
2838 }
2839 }
2840
2841 else if( STARTS_WITH_CI(papszArgv[i], "-colorinterp_") && papszArgv[i+1] )
2842 {
2843 int nIndex = atoi(papszArgv[i] + strlen("-colorinterp_"));
2844 if( nIndex <= 0 || nIndex > 65535 )
2845 {
2846 CPLError(CE_Failure, CPLE_NotSupported,
2847 "Invalid parameter name: %s", papszArgv[i]);
2848 GDALTranslateOptionsFree(psOptions);
2849 return nullptr;
2850 }
2851 nIndex --;
2852
2853 if( nIndex >= psOptions->nColorInterpSize )
2854 {
2855 psOptions->panColorInterp = static_cast<int *>(
2856 CPLRealloc(psOptions->panColorInterp,
2857 (nIndex + 1) * sizeof(int)));
2858 if( nIndex > psOptions->nColorInterpSize )
2859 {
2860 memset(psOptions->panColorInterp +
2861 psOptions->nColorInterpSize,
2862 0xFF, // -1
2863 sizeof(int) * (nIndex - psOptions->nColorInterpSize));
2864 }
2865 psOptions->nColorInterpSize = nIndex + 1;
2866 }
2867 ++i;
2868 psOptions->panColorInterp[nIndex] = GetColorInterp(papszArgv[i]);
2869 }
2870
2871
2872 // Undocumented option used by gdal_translate_fuzzer
2873 else if( i+1 < argc && EQUAL(papszArgv[i],"-limit_outsize") )
2874 {
2875 psOptions->nLimitOutSize = atoi(papszArgv[i+1]);
2876 i++;
2877 }
2878
2879 else if( i+1 < argc && EQUAL(papszArgv[i], "-if") )
2880 {
2881 i++;
2882 if( psOptionsForBinary )
2883 {
2884 if( GDALGetDriverByName(papszArgv[i]) == nullptr )
2885 {
2886 CPLError(CE_Warning, CPLE_AppDefined,
2887 "%s is not a recognized driver", papszArgv[i]);
2888 }
2889 psOptionsForBinary->papszAllowInputDrivers = CSLAddString(
2890 psOptionsForBinary->papszAllowInputDrivers, papszArgv[i] );
2891 }
2892 }
2893
2894 else if (EQUAL(papszArgv[i], "-noxmp"))
2895 {
2896 psOptions->bNoXMP = true;
2897 }
2898
2899
2900 else if( papszArgv[i][0] == '-' )
2901 {
2902 CPLError(CE_Failure, CPLE_NotSupported,
2903 "Unknown option name '%s'", papszArgv[i]);
2904 GDALTranslateOptionsFree(psOptions);
2905 return nullptr;
2906 }
2907 else if( !bGotSourceFilename )
2908 {
2909 bGotSourceFilename = true;
2910 if( psOptionsForBinary )
2911 psOptionsForBinary->pszSource = CPLStrdup(papszArgv[i]);
2912 }
2913 else if( !bGotDestFilename )
2914 {
2915 bGotDestFilename = true;
2916 if( psOptionsForBinary )
2917 psOptionsForBinary->pszDest = CPLStrdup(papszArgv[i]);
2918 }
2919 else
2920 {
2921 CPLError(CE_Failure, CPLE_NotSupported,
2922 "Too many command options '%s'", papszArgv[i]);
2923 GDALTranslateOptionsFree(psOptions);
2924 return nullptr;
2925 }
2926 }
2927
2928 if (psOptions->nGCPCount > 0 && psOptions->bNoGCP)
2929 {
2930 CPLError(CE_Failure, CPLE_IllegalArg,
2931 "-nogcp and -gcp cannot be used as the same time" );
2932 GDALTranslateOptionsFree(psOptions);
2933 return nullptr;
2934 }
2935
2936 if( bOutsideExplicitlySet &&
2937 psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
2938 psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0 )
2939 {
2940 CPLError(CE_Failure, CPLE_NotSupported,
2941 "-outsize %d %d invalid.", psOptions->nOXSizePixel, psOptions->nOYSizePixel);
2942 GDALTranslateOptionsFree(psOptions);
2943 return nullptr;
2944 }
2945
2946 if( psOptionsForBinary )
2947 {
2948 if( psOptions->pszFormat )
2949 psOptionsForBinary->pszFormat = CPLStrdup(psOptions->pszFormat);
2950 }
2951
2952 return psOptions;
2953 }
2954
2955 /************************************************************************/
2956 /* GDALTranslateOptionsFree() */
2957 /************************************************************************/
2958
2959 /**
2960 * Frees the GDALTranslateOptions struct.
2961 *
2962 * @param psOptions the options struct for GDALTranslate().
2963 *
2964 * @since GDAL 2.1
2965 */
2966
GDALTranslateOptionsFree(GDALTranslateOptions * psOptions)2967 void GDALTranslateOptionsFree(GDALTranslateOptions *psOptions)
2968 {
2969 if( psOptions == nullptr ) return;
2970
2971 CPLFree(psOptions->pszFormat);
2972 CPLFree(psOptions->panBandList);
2973 CSLDestroy(psOptions->papszCreateOptions);
2974 CPLFree(psOptions->pasScaleParams);
2975 CPLFree(psOptions->padfExponent);
2976 CSLDestroy(psOptions->papszMetadataOptions);
2977 CPLFree(psOptions->pszOutputSRS);
2978 if( psOptions->nGCPCount )
2979 GDALDeinitGCPs(psOptions->nGCPCount, psOptions->pasGCPs);
2980 CPLFree(psOptions->pasGCPs);
2981 CPLFree(psOptions->pszResampling);
2982 CPLFree(psOptions->pszProjSRS);
2983 CPLFree(psOptions->panColorInterp);
2984
2985 CPLFree(psOptions);
2986 }
2987
2988 /************************************************************************/
2989 /* GDALTranslateOptionsSetProgress() */
2990 /************************************************************************/
2991
2992 /**
2993 * Set a progress function.
2994 *
2995 * @param psOptions the options struct for GDALTranslate().
2996 * @param pfnProgress the progress callback.
2997 * @param pProgressData the user data for the progress callback.
2998 *
2999 * @since GDAL 2.1
3000 */
3001
GDALTranslateOptionsSetProgress(GDALTranslateOptions * psOptions,GDALProgressFunc pfnProgress,void * pProgressData)3002 void GDALTranslateOptionsSetProgress( GDALTranslateOptions *psOptions,
3003 GDALProgressFunc pfnProgress, void *pProgressData )
3004 {
3005 psOptions->pfnProgress = pfnProgress;
3006 psOptions->pProgressData = pProgressData;
3007 if( pfnProgress == GDALTermProgress )
3008 psOptions->bQuiet = false;
3009 }
3010