LCOV - code coverage report
Current view: top level - apps - gdal_translate_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1441 1655 87.1 %
Date: 2025-05-14 21:55:07 Functions: 29 31 93.5 %

          Line data    Source code
       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             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "gdal_utils.h"
      17             : #include "gdal_utils_priv.h"
      18             : #include "gdalargumentparser.h"
      19             : 
      20             : #include <cmath>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : 
      24             : #include <algorithm>
      25             : #include <array>
      26             : #include <limits>
      27             : #include <set>
      28             : 
      29             : #include "commonutils.h"
      30             : #include "cpl_conv.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_json.h"
      33             : #include "cpl_progress.h"
      34             : #include "cpl_string.h"
      35             : #include "cpl_vsi.h"
      36             : #include "gdal.h"
      37             : #include "gdal_priv.h"
      38             : #include "gdal_priv_templates.hpp"
      39             : #include "gdal_rat.h"
      40             : #include "gdal_vrt.h"
      41             : #include "ogr_core.h"
      42             : #include "ogr_spatialref.h"
      43             : #include "vrtdataset.h"
      44             : 
      45             : static void AttachMetadata(GDALDatasetH, const CPLStringList &);
      46             : static void AttachDomainMetadata(GDALDatasetH, const CPLStringList &);
      47             : 
      48             : static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
      49             :                          int bCanCopyStatsMetadata, int bCopyScale,
      50             :                          int bCopyNoData, bool bCopyRAT,
      51             :                          const GDALTranslateOptions *psOptions);
      52             : 
      53             : typedef enum
      54             : {
      55             :     MASK_DISABLED,
      56             :     MASK_AUTO,
      57             :     MASK_USER
      58             : } MaskMode;
      59             : 
      60             : /************************************************************************/
      61             : /*                         GDALTranslateScaleParams                     */
      62             : /************************************************************************/
      63             : 
      64             : /** scaling parameters for use in GDALTranslateOptions.
      65             :  */
      66             : struct GDALTranslateScaleParams
      67             : {
      68             :     /*! scaling is done only if it is set to TRUE. This is helpful when there is
      69             :        a need to scale only certain bands. */
      70             :     bool bScale = false;
      71             : 
      72             :     /*! the range of input pixel values which need to be scaled.
      73             :         If not set, the input range is automatically computed from the source data. */
      74             :     double dfScaleSrcMin = std::numeric_limits<double>::quiet_NaN();
      75             :     double dfScaleSrcMax = std::numeric_limits<double>::quiet_NaN();
      76             : 
      77             :     /*! the range of output pixel values. */
      78             :     double dfScaleDstMin = std::numeric_limits<double>::quiet_NaN();
      79             :     double dfScaleDstMax = std::numeric_limits<double>::quiet_NaN();
      80             : };
      81             : 
      82             : /************************************************************************/
      83             : /*                         GDALTranslateOptions                         */
      84             : /************************************************************************/
      85             : 
      86             : /** Options for use with GDALTranslate(). GDALTranslateOptions* must be
      87             :  * allocated and freed with GDALTranslateOptionsNew() and
      88             :  * GDALTranslateOptionsFree() respectively.
      89             :  */
      90             : struct GDALTranslateOptions
      91             : {
      92             : 
      93             :     /*! output format. Use the short format name. */
      94             :     std::string osFormat{};
      95             : 
      96             :     /*! allow or suppress progress monitor and other non-error output */
      97             :     bool bQuiet = false;
      98             : 
      99             :     /*! the progress function to use */
     100             :     GDALProgressFunc pfnProgress = GDALDummyProgress;
     101             : 
     102             :     /*! pointer to the progress data variable */
     103             :     void *pProgressData = nullptr;
     104             : 
     105             :     /*! for the output bands to be of the indicated data type */
     106             :     GDALDataType eOutputType = GDT_Unknown;
     107             : 
     108             :     /*! Used only by parser logic */
     109             :     bool bParsedMaskArgument = false;
     110             : 
     111             :     MaskMode eMaskMode = MASK_AUTO;
     112             : 
     113             :     /*! number of input bands to write to the output file, or to reorder bands
     114             :      */
     115             :     int nBandCount = 0;
     116             : 
     117             :     /*! list of input bands to write to the output file, or to reorder bands.
     118             :        The value 1 corresponds to the 1st band. */
     119             :     std::vector<int> anBandList{}; /* negative value of panBandList[i] means
     120             :                                       mask band of ABS(panBandList[i]) */
     121             : 
     122             :     /*! size of the output file. GDALTranslateOptions::nOXSizePixel is in pixels
     123             :        and GDALTranslateOptions::nOYSizePixel is in lines. If one of the two
     124             :        values is set to 0, its value will be determined from the other one,
     125             :        while maintaining the aspect ratio of the source dataset */
     126             :     int nOXSizePixel = 0;
     127             :     int nOYSizePixel = 0;
     128             : 
     129             :     /*! size of the output file. GDALTranslateOptions::dfOXSizePct and
     130             :        GDALTranslateOptions::dfOYSizePct are fraction of the input image size.
     131             :        The value 100 means 100%. If one of the two values is set to 0, its value
     132             :        will be determined from the other one, while maintaining the aspect ratio
     133             :        of the source dataset */
     134             :     double dfOXSizePct = 0;
     135             :     double dfOYSizePct = 0;
     136             : 
     137             :     /*! list of creation options to the output format driver */
     138             :     CPLStringList aosCreateOptions{};
     139             : 
     140             :     /*! subwindow from the source image for copying based on pixel/line location
     141             :      */
     142             :     struct PixelLineWindow
     143             :     {
     144             :         double dfXOff{0};
     145             :         double dfYOff{0};
     146             :         double dfXSize{0};
     147             :         double dfYSize{0};
     148             :     };
     149             : 
     150             :     PixelLineWindow srcWin{};
     151             : 
     152             :     /*! don't be forgiving of mismatches and lost data when translating to the
     153             :      * output format */
     154             :     bool bStrict = false;
     155             : 
     156             :     /*! apply the scale/offset metadata for the bands to convert scaled values
     157             :      * to unscaled values. It is also often necessary to reset the output
     158             :      * datatype with GDALTranslateOptions::eOutputType */
     159             :     bool bUnscale = false;
     160             : 
     161             :     bool bSetScale = false;
     162             : 
     163             :     double dfScale = 1;
     164             : 
     165             :     bool bSetOffset = false;
     166             : 
     167             :     double dfOffset = 0;
     168             : 
     169             :     /*! the list of scale parameters for each band. */
     170             :     std::vector<GDALTranslateScaleParams> asScaleParams{};
     171             : 
     172             :     /*! It is set to TRUE, when scale parameters are specific to each band */
     173             :     bool bHasUsedExplicitScaleBand = false;
     174             : 
     175             :     bool bNoClip = false;
     176             : 
     177             :     /*! to apply non-linear scaling with a power function. It is the list of
     178             :        exponents of the power function (must be positive). This option must be
     179             :        used with GDALTranslateOptions::asScaleParams. If
     180             :         GDALTranslateOptions::adfExponent.size() is 1, it is applied to all
     181             :        bands of the output image. */
     182             :     std::vector<double> adfExponent{};
     183             : 
     184             :     bool bHasUsedExplicitExponentBand = false;
     185             : 
     186             :     /*! list of metadata key and value to set on the output dataset if possible. */
     187             :     CPLStringList aosMetadataOptions{};
     188             : 
     189             :     /*! list of metadata key and value in a domain to set on the output dataset if possible. */
     190             :     CPLStringList aosDomainMetadataOptions{};
     191             : 
     192             :     /*! override the projection for the output file. The SRS may be any of the
     193             :        usual GDAL/OGR forms, complete WKT, PROJ.4, EPSG:n or a file containing
     194             :        the WKT. */
     195             :     std::string osOutputSRS{};
     196             : 
     197             :     /*! Coordinate epoch of output SRS */
     198             :     double dfOutputCoordinateEpoch = 0;
     199             : 
     200             :     /*! does not copy source GCP into destination dataset (when TRUE) */
     201             :     bool bNoGCP = false;
     202             : 
     203             :     /*! number of GCPS to be added to the output dataset */
     204             :     int nGCPCount = 0;
     205             : 
     206             :     /*! list of GCPs to be added to the output dataset */
     207             :     GDAL_GCP *pasGCPs = nullptr;
     208             : 
     209             :     /*! assign/override the georeferenced bounds of the output file. This
     210             :        assigns georeferenced bounds to the output file, ignoring what would have
     211             :        been derived from the source file. So this does not cause reprojection to
     212             :        the specified SRS. */
     213             :     std::array<double, 4> adfULLR{{0, 0, 0, 0}};
     214             : 
     215             :     /*! assign/override the geotransform of the output file. This
     216             :        assigns a geotransform to the output file, ignoring what would have
     217             :        been derived from the source file. So this does not cause reprojection to
     218             :        the specified SRS. */
     219             :     std::array<double, 6> adfGT{{0, 0, 0, 0, 0, 0}};
     220             : 
     221             :     /*! set a nodata value specified in GDALTranslateOptions::osNoData to the
     222             :      * output bands */
     223             :     bool bSetNoData = 0;
     224             : 
     225             :     /*! avoid setting a nodata value to the output file if one exists for the
     226             :      * source file */
     227             :     bool bUnsetNoData = 0;
     228             : 
     229             :     /*! Assign a specified nodata value to output bands (
     230             :        GDALTranslateOptions::bSetNoData option should be set). Note that if the
     231             :        input dataset has a nodata value, this does not cause pixel values that
     232             :        are equal to that nodata value to be changed to the value specified. */
     233             :     std::string osNoData{};
     234             : 
     235             :     /*! to expose a dataset with 1 band with a color table as a dataset with
     236             :         3 (RGB) or 4 (RGBA) bands. Useful for output drivers such as JPEG,
     237             :         JPEG2000, MrSID, ECW that don't support color indexed datasets.
     238             :         The 1 value enables to expand a dataset with a color table that only
     239             :         contains gray levels to a gray indexed dataset. */
     240             :     int nRGBExpand = 0;
     241             : 
     242             :     int nMaskBand = 0; /* negative value means mask band of ABS(nMaskBand) */
     243             : 
     244             :     /*! force recomputation of statistics */
     245             :     bool bStats = false;
     246             : 
     247             :     bool bApproxStats = false;
     248             : 
     249             :     /*! If this option is set, GDALTranslateOptions::adfSrcWin or
     250             :        (GDALTranslateOptions::dfULX, GDALTranslateOptions::dfULY,
     251             :        GDALTranslateOptions::dfLRX, GDALTranslateOptions::dfLRY) values that
     252             :        falls partially outside the source raster extent will be considered as an
     253             :        error. The default behavior is to accept such requests. */
     254             :     bool bErrorOnPartiallyOutside = false;
     255             : 
     256             :     /*! Same as bErrorOnPartiallyOutside, except that the criterion for
     257             :         erroring out is when the request falls completely outside the
     258             :         source raster extent. */
     259             :     bool bErrorOnCompletelyOutside = false;
     260             : 
     261             :     /*! does not copy source RAT into destination dataset (when TRUE) */
     262             :     bool bNoRAT = false;
     263             : 
     264             :     /*! resampling algorithm
     265             :         nearest (default), bilinear, cubic, cubicspline, lanczos, average, mode
     266             :      */
     267             :     std::string osResampling{};
     268             : 
     269             :     /*! target resolution. The values must be expressed in georeferenced units.
     270             :         Both must be positive values. This is exclusive with
     271             :        GDALTranslateOptions::nOXSizePixel (or
     272             :        GDALTranslateOptions::dfOXSizePct), GDALTranslateOptions::nOYSizePixel
     273             :         (or GDALTranslateOptions::dfOYSizePct), GDALTranslateOptions::adfULLR,
     274             :         and GDALTranslateOptions::adfGT.
     275             :      */
     276             :     double dfXRes = 0;
     277             :     double dfYRes = 0;
     278             : 
     279             :     /*! subwindow from the source image for copying (like
     280             :        GDALTranslateOptions::adfSrcWin) but with the corners given in
     281             :        georeferenced coordinates (by default expressed in the SRS of the
     282             :        dataset. Can be changed with osProjSRS) */
     283             :     double dfULX = 0;
     284             :     double dfULY = 0;
     285             :     double dfLRX = 0;
     286             :     double dfLRY = 0;
     287             : 
     288             :     /*! SRS in which to interpret the coordinates given with
     289             :        GDALTranslateOptions::dfULX, GDALTranslateOptions::dfULY,
     290             :        GDALTranslateOptions::dfLRX, GDALTranslateOptions::dfLRY. The SRS may be
     291             :        any of the usual GDAL/OGR forms, complete WKT, PROJ.4, EPSG:n or a file
     292             :        containing the WKT. Note that this does not cause reprojection of the
     293             :         dataset to the specified SRS. */
     294             :     std::string osProjSRS{};
     295             : 
     296             :     int nLimitOutSize = 0;
     297             : 
     298             :     // Array of color interpretations per band. Should be a GDALColorInterp
     299             :     // value, or -1 if no override.
     300             :     std::vector<int> anColorInterp{};
     301             : 
     302             :     /*! does not copy source XMP into destination dataset (when TRUE) */
     303             :     bool bNoXMP = false;
     304             : 
     305             :     /*! overview level of source file to be used */
     306             :     int nOvLevel = OVR_LEVEL_AUTO;
     307             : 
     308             :     /*! set to true to prevent overwriting existing dataset */
     309             :     bool bNoOverwrite = false;
     310             : 
     311        2706 :     GDALTranslateOptions() = default;
     312             :     ~GDALTranslateOptions();
     313             :     GDALTranslateOptions *Clone() const;
     314             : 
     315             :   private:
     316        2678 :     GDALTranslateOptions(const GDALTranslateOptions &) = default;
     317             :     GDALTranslateOptions &operator=(const GDALTranslateOptions &) = delete;
     318             : };
     319             : 
     320             : /************************************************************************/
     321             : /*             GDALTranslateOptions::~GDALTranslateOptions()            */
     322             : /************************************************************************/
     323             : 
     324        5379 : GDALTranslateOptions::~GDALTranslateOptions()
     325             : {
     326        5379 :     if (nGCPCount)
     327          18 :         GDALDeinitGCPs(nGCPCount, pasGCPs);
     328        5379 :     CPLFree(pasGCPs);
     329        5379 : }
     330             : 
     331             : /************************************************************************/
     332             : /*                    GDALTranslateOptions::Clone(()                    */
     333             : /************************************************************************/
     334             : 
     335        2678 : GDALTranslateOptions *GDALTranslateOptions::Clone() const
     336             : {
     337        2678 :     GDALTranslateOptions *psOptions = new GDALTranslateOptions(*this);
     338        2678 :     if (nGCPCount)
     339           9 :         psOptions->pasGCPs = GDALDuplicateGCPs(nGCPCount, pasGCPs);
     340        2678 :     return psOptions;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                              SrcToDst()                              */
     345             : /************************************************************************/
     346             : 
     347          52 : static void SrcToDst(double dfX, double dfY, double dfSrcXOff, double dfSrcYOff,
     348             :                      double dfSrcXSize, double dfSrcYSize, double dfDstXOff,
     349             :                      double dfDstYOff, double dfDstXSize, double dfDstYSize,
     350             :                      double &dfXOut, double &dfYOut)
     351             : 
     352             : {
     353          52 :     dfXOut = ((dfX - dfSrcXOff) / dfSrcXSize) * dfDstXSize + dfDstXOff;
     354          52 :     dfYOut = ((dfY - dfSrcYOff) / dfSrcYSize) * dfDstYSize + dfDstYOff;
     355          52 : }
     356             : 
     357             : /************************************************************************/
     358             : /*                          GetSrcDstWindow()                           */
     359             : /************************************************************************/
     360             : 
     361        1782 : static bool FixSrcDstWindow(GDALTranslateOptions::PixelLineWindow &srcWin,
     362             :                             GDALTranslateOptions::PixelLineWindow &dstWin,
     363             :                             int nSrcRasterXSize, int nSrcRasterYSize)
     364             : 
     365             : {
     366        1782 :     const double dfSrcXOff = srcWin.dfXOff;
     367        1782 :     const double dfSrcYOff = srcWin.dfYOff;
     368        1782 :     const double dfSrcXSize = srcWin.dfXSize;
     369        1782 :     const double dfSrcYSize = srcWin.dfYSize;
     370             : 
     371        1782 :     const double dfDstXOff = dstWin.dfXOff;
     372        1782 :     const double dfDstYOff = dstWin.dfYOff;
     373        1782 :     const double dfDstXSize = dstWin.dfXSize;
     374        1782 :     const double dfDstYSize = dstWin.dfYSize;
     375             : 
     376        1782 :     bool bModifiedX = false;
     377        1782 :     bool bModifiedY = false;
     378             : 
     379        1782 :     double dfModifiedSrcXOff = dfSrcXOff;
     380        1782 :     double dfModifiedSrcYOff = dfSrcYOff;
     381             : 
     382        1782 :     double dfModifiedSrcXSize = dfSrcXSize;
     383        1782 :     double dfModifiedSrcYSize = dfSrcYSize;
     384             : 
     385             :     /* -------------------------------------------------------------------- */
     386             :     /*      Clamp within the bounds of the available source data.           */
     387             :     /* -------------------------------------------------------------------- */
     388        1782 :     if (dfModifiedSrcXOff < 0)
     389             :     {
     390           9 :         dfModifiedSrcXSize += dfModifiedSrcXOff;
     391           9 :         dfModifiedSrcXOff = 0;
     392             : 
     393           9 :         bModifiedX = true;
     394             :     }
     395             : 
     396        1782 :     if (dfModifiedSrcYOff < 0)
     397             :     {
     398           4 :         dfModifiedSrcYSize += dfModifiedSrcYOff;
     399           4 :         dfModifiedSrcYOff = 0;
     400           4 :         bModifiedY = true;
     401             :     }
     402             : 
     403        1782 :     if (dfModifiedSrcXOff + dfModifiedSrcXSize > nSrcRasterXSize)
     404             :     {
     405          20 :         dfModifiedSrcXSize = nSrcRasterXSize - dfModifiedSrcXOff;
     406          20 :         bModifiedX = true;
     407             :     }
     408             : 
     409        1782 :     if (dfModifiedSrcYOff + dfModifiedSrcYSize > nSrcRasterYSize)
     410             :     {
     411          24 :         dfModifiedSrcYSize = nSrcRasterYSize - dfModifiedSrcYOff;
     412          24 :         bModifiedY = true;
     413             :     }
     414             : 
     415             :     /* -------------------------------------------------------------------- */
     416             :     /*      Don't do anything if the requesting region is completely off    */
     417             :     /*      the source image.                                               */
     418             :     /* -------------------------------------------------------------------- */
     419        1782 :     if (dfModifiedSrcXOff >= nSrcRasterXSize ||
     420        1782 :         dfModifiedSrcYOff >= nSrcRasterYSize || dfModifiedSrcXSize <= 0 ||
     421             :         dfModifiedSrcYSize <= 0)
     422             :     {
     423           2 :         return false;
     424             :     }
     425             : 
     426        1780 :     srcWin.dfXOff = dfModifiedSrcXOff;
     427        1780 :     srcWin.dfYOff = dfModifiedSrcYOff;
     428        1780 :     srcWin.dfXSize = dfModifiedSrcXSize;
     429        1780 :     srcWin.dfYSize = dfModifiedSrcYSize;
     430             : 
     431             :     /* -------------------------------------------------------------------- */
     432             :     /*      If we haven't had to modify the source rectangle, then the      */
     433             :     /*      destination rectangle must be the whole region.                 */
     434             :     /* -------------------------------------------------------------------- */
     435        1780 :     if (!bModifiedX && !bModifiedY)
     436        1754 :         return true;
     437             : 
     438             :     /* -------------------------------------------------------------------- */
     439             :     /*      Now transform this possibly reduced request back into the       */
     440             :     /*      destination buffer coordinates in case the output region is     */
     441             :     /*      less than the whole buffer.                                     */
     442             :     /* -------------------------------------------------------------------- */
     443             :     double dfDstULX, dfDstULY, dfDstLRX, dfDstLRY;
     444             : 
     445          26 :     SrcToDst(dfModifiedSrcXOff, dfModifiedSrcYOff, dfSrcXOff, dfSrcYOff,
     446             :              dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
     447             :              dfDstYSize, dfDstULX, dfDstULY);
     448          26 :     SrcToDst(dfModifiedSrcXOff + dfModifiedSrcXSize,
     449             :              dfModifiedSrcYOff + dfModifiedSrcYSize, dfSrcXOff, dfSrcYOff,
     450             :              dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
     451             :              dfDstYSize, dfDstLRX, dfDstLRY);
     452             : 
     453          26 :     double dfModifiedDstXOff = dfDstXOff;
     454          26 :     double dfModifiedDstYOff = dfDstYOff;
     455          26 :     double dfModifiedDstXSize = dfDstXSize;
     456          26 :     double dfModifiedDstYSize = dfDstYSize;
     457             : 
     458          26 :     if (bModifiedX)
     459             :     {
     460          24 :         dfModifiedDstXOff = dfDstULX - dfDstXOff;
     461          24 :         dfModifiedDstXSize = (dfDstLRX - dfDstXOff) - dfModifiedDstXOff;
     462             : 
     463          24 :         dfModifiedDstXOff = std::max(0.0, dfModifiedDstXOff);
     464          24 :         if (dfModifiedDstXOff + dfModifiedDstXSize > dfDstXSize)
     465           0 :             dfModifiedDstXSize = dfDstXSize - dfModifiedDstXOff;
     466             :     }
     467             : 
     468          26 :     if (bModifiedY)
     469             :     {
     470          23 :         dfModifiedDstYOff = dfDstULY - dfDstYOff;
     471          23 :         dfModifiedDstYSize = (dfDstLRY - dfDstYOff) - dfModifiedDstYOff;
     472             : 
     473          23 :         dfModifiedDstYOff = std::max(0.0, dfModifiedDstYOff);
     474          23 :         if (dfModifiedDstYOff + dfModifiedDstYSize > dfDstYSize)
     475           0 :             dfModifiedDstYSize = dfDstYSize - dfModifiedDstYOff;
     476             :     }
     477             : 
     478          26 :     if (dfModifiedDstXSize <= 0.0 || dfModifiedDstYSize <= 0.0)
     479             :     {
     480           0 :         return false;
     481             :     }
     482             : 
     483          26 :     dstWin.dfXOff = dfModifiedDstXOff;
     484          26 :     dstWin.dfYOff = dfModifiedDstYOff;
     485          26 :     dstWin.dfXSize = dfModifiedDstXSize;
     486          26 :     dstWin.dfYSize = dfModifiedDstYSize;
     487             : 
     488          26 :     return true;
     489             : }
     490             : 
     491             : /************************************************************************/
     492             : /*                        GDALTranslateFlush()                          */
     493             : /************************************************************************/
     494             : 
     495        2341 : static GDALDatasetH GDALTranslateFlush(GDALDatasetH hOutDS)
     496             : {
     497        2341 :     if (hOutDS != nullptr)
     498             :     {
     499        2247 :         CPLErr eErrBefore = CPLGetLastErrorType();
     500        2247 :         GDALFlushCache(hOutDS);
     501        2247 :         if (eErrBefore == CE_None && CPLGetLastErrorType() != CE_None)
     502             :         {
     503           1 :             GDALClose(hOutDS);
     504           1 :             hOutDS = nullptr;
     505             :         }
     506             :     }
     507        2341 :     return hOutDS;
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /*                    EditISIS3MetadataForBandChange()                  */
     512             : /************************************************************************/
     513             : 
     514           1 : static CPLJSONObject Clone(const CPLJSONObject &obj)
     515             : {
     516           2 :     auto serialized = obj.Format(CPLJSONObject::PrettyFormat::Plain);
     517           2 :     CPLJSONDocument oJSONDocument;
     518           1 :     const GByte *pabyData = reinterpret_cast<const GByte *>(serialized.c_str());
     519           1 :     oJSONDocument.LoadMemory(pabyData);
     520           2 :     return oJSONDocument.GetRoot();
     521             : }
     522             : 
     523           4 : static void ReworkArray(CPLJSONObject &container, const CPLJSONObject &obj,
     524             :                         int nSrcBandCount,
     525             :                         const GDALTranslateOptions *psOptions)
     526             : {
     527           8 :     auto oArray = obj.ToArray();
     528           4 :     if (oArray.Size() == nSrcBandCount)
     529             :     {
     530           8 :         CPLJSONArray oNewArray;
     531           8 :         for (int nBand : psOptions->anBandList)
     532             :         {
     533           4 :             const int iSrcIdx = nBand - 1;
     534           4 :             oNewArray.Add(oArray[iSrcIdx]);
     535             :         }
     536           8 :         const auto childName(obj.GetName());
     537           4 :         container.Delete(childName);
     538           4 :         container.Add(childName, oNewArray);
     539             :     }
     540           4 : }
     541             : 
     542             : static CPLString
     543           1 : EditISIS3MetadataForBandChange(const char *pszJSON, int nSrcBandCount,
     544             :                                const GDALTranslateOptions *psOptions)
     545             : {
     546           2 :     CPLJSONDocument oJSONDocument;
     547           1 :     const GByte *pabyData = reinterpret_cast<const GByte *>(pszJSON);
     548           1 :     if (!oJSONDocument.LoadMemory(pabyData))
     549             :     {
     550           0 :         return CPLString();
     551             :     }
     552             : 
     553           2 :     auto oRoot = oJSONDocument.GetRoot();
     554           1 :     if (!oRoot.IsValid())
     555             :     {
     556           0 :         return CPLString();
     557             :     }
     558             : 
     559           2 :     auto oBandBin = oRoot.GetObj("IsisCube/BandBin");
     560           1 :     if (oBandBin.IsValid() && oBandBin.GetType() == CPLJSONObject::Type::Object)
     561             :     {
     562             :         // Backup original BandBin object
     563           1 :         oRoot.GetObj("IsisCube").Add("OriginalBandBin", Clone(oBandBin));
     564             : 
     565             :         // Iterate over BandBin members and reorder/resize its arrays that
     566             :         // have the same number of elements than the number of bands of the
     567             :         // source dataset.
     568           7 :         for (auto &child : oBandBin.GetChildren())
     569             :         {
     570           6 :             if (child.GetType() == CPLJSONObject::Type::Array)
     571             :             {
     572           3 :                 ReworkArray(oBandBin, child, nSrcBandCount, psOptions);
     573             :             }
     574           3 :             else if (child.GetType() == CPLJSONObject::Type::Object)
     575             :             {
     576           3 :                 auto oValue = child.GetObj("value");
     577           3 :                 auto oUnit = child.GetObj("unit");
     578           1 :                 if (oValue.GetType() == CPLJSONObject::Type::Array)
     579             :                 {
     580           1 :                     ReworkArray(child, oValue, nSrcBandCount, psOptions);
     581             :                 }
     582             :             }
     583             :         }
     584             :     }
     585             : 
     586           2 :     return oRoot.Format(CPLJSONObject::PrettyFormat::Pretty);
     587             : }
     588             : 
     589             : /************************************************************************/
     590             : /*                             GDALTranslate()                          */
     591             : /************************************************************************/
     592             : 
     593             : /* clang-format off */
     594             : /**
     595             :  * Converts raster data between different formats.
     596             :  *
     597             :  * This is the equivalent of the
     598             :  * <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
     599             :  *
     600             :  * GDALTranslateOptions* must be allocated and freed with
     601             :  * GDALTranslateOptionsNew() and GDALTranslateOptionsFree() respectively.
     602             :  *
     603             :  * @param pszDest the destination dataset path.
     604             :  * @param hSrcDataset the source dataset handle.
     605             :  * @param psOptionsIn the options struct returned by GDALTranslateOptionsNew()
     606             :  * or NULL.
     607             :  * @param pbUsageError pointer to a integer output variable to store if any
     608             :  * usage error has occurred or NULL.
     609             :  * @return the output dataset (new dataset that must be closed using
     610             :  * GDALClose()) or NULL in case of error. If the output
     611             :  * format is a VRT dataset, then the returned VRT dataset has a reference to
     612             :  * hSrcDataset. Hence hSrcDataset should be closed after the returned dataset
     613             :  * if using GDALClose().
     614             :  * A safer alternative is to use GDALReleaseDataset() instead of using
     615             :  * GDALClose(), in which case you can close datasets in any order.
     616             :  *
     617             :  * @since GDAL 2.1
     618             :  */
     619             : /* clang-format on */
     620             : 
     621        2689 : GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
     622             :                            const GDALTranslateOptions *psOptionsIn,
     623             :                            int *pbUsageError)
     624             : 
     625             : {
     626        2689 :     CPLErrorReset();
     627        2689 :     if (hSrcDataset == nullptr)
     628             :     {
     629           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
     630             : 
     631           0 :         if (pbUsageError)
     632           0 :             *pbUsageError = TRUE;
     633           0 :         return nullptr;
     634             :     }
     635        2689 :     if (pszDest == nullptr)
     636             :     {
     637           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
     638             : 
     639           0 :         if (pbUsageError)
     640           0 :             *pbUsageError = TRUE;
     641           0 :         return nullptr;
     642             :     }
     643             : 
     644             :     GDALTranslateOptions *psOptions =
     645        2689 :         (psOptionsIn) ? psOptionsIn->Clone()
     646          11 :                       : GDALTranslateOptionsNew(nullptr, nullptr);
     647             : 
     648        2689 :     GDALDatasetH hOutDS = nullptr;
     649        2689 :     bool bGotBounds = false;
     650        2689 :     bool bGotGeoTransform = false;
     651             : 
     652        2689 :     if (pbUsageError)
     653        2111 :         *pbUsageError = FALSE;
     654             : 
     655        5362 :     if (psOptions->adfULLR[0] != 0.0 || psOptions->adfULLR[1] != 0.0 ||
     656        5362 :         psOptions->adfULLR[2] != 0.0 || psOptions->adfULLR[3] != 0.0)
     657          20 :         bGotBounds = true;
     658             : 
     659        5377 :     if (psOptions->adfGT[0] != 0.0 || psOptions->adfGT[1] != 0.0 ||
     660        2686 :         psOptions->adfGT[2] != 0.0 || psOptions->adfGT[3] != 0.0 ||
     661        5377 :         psOptions->adfGT[4] != 0.0 || psOptions->adfGT[5] != 0.0)
     662           3 :         bGotGeoTransform = true;
     663             : 
     664        2689 :     GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDataset);
     665        2689 :     const char *pszSource = poSrcDS->GetDescription();
     666             : 
     667        2689 :     if (strcmp(pszSource, pszDest) == 0 && pszSource[0] != '\0' &&
     668           0 :         poSrcDS->GetDriver() != GDALGetDriverByName("MEM"))
     669             :     {
     670           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     671             :                  "Source and destination datasets must be different.");
     672             : 
     673           0 :         if (pbUsageError)
     674           0 :             *pbUsageError = TRUE;
     675           0 :         GDALTranslateOptionsFree(psOptions);
     676           0 :         return nullptr;
     677             :     }
     678             : 
     679        5378 :     CPLString osProjSRS;
     680             : 
     681        2689 :     if (!psOptions->osProjSRS.empty())
     682             :     {
     683           8 :         OGRSpatialReference oSRS;
     684           8 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     685             : 
     686           8 :         if (oSRS.SetFromUserInput(psOptions->osProjSRS.c_str()) != OGRERR_NONE)
     687             :         {
     688           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     689             :                      "Failed to process SRS definition: %s",
     690             :                      psOptions->osProjSRS.c_str());
     691           0 :             GDALTranslateOptionsFree(psOptions);
     692           0 :             return nullptr;
     693             :         }
     694             : 
     695           8 :         char *pszSRS = nullptr;
     696           8 :         oSRS.exportToWkt(&pszSRS);
     697           8 :         if (pszSRS)
     698           8 :             osProjSRS = pszSRS;
     699           8 :         CPLFree(pszSRS);
     700             :     }
     701             : 
     702        2802 :     if (!psOptions->osOutputSRS.empty() && psOptions->osOutputSRS != "null" &&
     703         113 :         psOptions->osOutputSRS != "none")
     704             :     {
     705         111 :         OGRSpatialReference oOutputSRS;
     706         111 :         if (oOutputSRS.SetFromUserInput(psOptions->osOutputSRS.c_str()) !=
     707             :             OGRERR_NONE)
     708             :         {
     709           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     710             :                      "Failed to process SRS definition: %s",
     711             :                      psOptions->osOutputSRS.c_str());
     712           0 :             GDALTranslateOptionsFree(psOptions);
     713           0 :             return nullptr;
     714             :         }
     715             :     }
     716             : 
     717             :     /* -------------------------------------------------------------------- */
     718             :     /*      Check that incompatible options are not used                    */
     719             :     /* -------------------------------------------------------------------- */
     720             : 
     721        2689 :     if ((psOptions->nOXSizePixel != 0 || psOptions->dfOXSizePct != 0.0 ||
     722        2468 :          psOptions->nOYSizePixel != 0 || psOptions->dfOYSizePct != 0.0) &&
     723         223 :         (psOptions->dfXRes != 0 && psOptions->dfYRes != 0))
     724             :     {
     725           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     726             :                  "-outsize and -tr options cannot be used at the same time.");
     727           0 :         if (pbUsageError)
     728           0 :             *pbUsageError = TRUE;
     729           0 :         GDALTranslateOptionsFree(psOptions);
     730           0 :         return nullptr;
     731             :     }
     732        2689 :     if ((bGotBounds | bGotGeoTransform) &&
     733          23 :         (psOptions->dfXRes != 0 && psOptions->dfYRes != 0))
     734             :     {
     735           0 :         CPLError(
     736             :             CE_Failure, CPLE_IllegalArg,
     737             :             "-a_ullr or -a_gt options cannot be used at the same time as -tr.");
     738           0 :         if (pbUsageError)
     739           0 :             *pbUsageError = TRUE;
     740           0 :         GDALTranslateOptionsFree(psOptions);
     741           0 :         return nullptr;
     742             :     }
     743        2689 :     if (bGotBounds && bGotGeoTransform)
     744             :     {
     745           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     746             :                  "-a_ullr and -a_gt options cannot be used at the same time.");
     747           0 :         if (pbUsageError)
     748           0 :             *pbUsageError = TRUE;
     749           0 :         GDALTranslateOptionsFree(psOptions);
     750           0 :         return nullptr;
     751             :     }
     752             : 
     753             :     /* -------------------------------------------------------------------- */
     754             :     /*      Collect some information from the source file.                  */
     755             :     /* -------------------------------------------------------------------- */
     756        2689 :     if (psOptions->srcWin.dfXSize == 0 && psOptions->srcWin.dfYSize == 0)
     757             :     {
     758        1780 :         psOptions->srcWin.dfXSize = poSrcDS->GetRasterXSize();
     759        1780 :         psOptions->srcWin.dfYSize = poSrcDS->GetRasterYSize();
     760             :     }
     761             : 
     762             :     /* -------------------------------------------------------------------- */
     763             :     /*      Build band list to translate                                    */
     764             :     /* -------------------------------------------------------------------- */
     765        2689 :     bool bAllBandsInOrder = true;
     766             : 
     767        2689 :     if (psOptions->anBandList.empty())
     768             :     {
     769             : 
     770        2446 :         psOptions->nBandCount = poSrcDS->GetRasterCount();
     771        2446 :         if ((psOptions->nBandCount == 0) && (psOptions->bStrict))
     772             :         {
     773             :             // if not strict then the driver can fail if it doesn't support zero
     774             :             // bands
     775           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     776             :                      "Input file has no bands, and so cannot be translated.");
     777           0 :             GDALTranslateOptionsFree(psOptions);
     778           0 :             return nullptr;
     779             :         }
     780             : 
     781        2446 :         psOptions->anBandList.resize(psOptions->nBandCount);
     782       73065 :         for (int i = 0; i < psOptions->nBandCount; i++)
     783       70619 :             psOptions->anBandList[i] = i + 1;
     784             :     }
     785             :     else
     786             :     {
     787         836 :         for (int i = 0; i < psOptions->nBandCount; i++)
     788             :         {
     789         593 :             if (std::abs(psOptions->anBandList[i]) > poSrcDS->GetRasterCount())
     790             :             {
     791           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     792             :                          "Band %d requested, but only bands 1 to %d available.",
     793           0 :                          std::abs(psOptions->anBandList[i]),
     794             :                          poSrcDS->GetRasterCount());
     795           0 :                 GDALTranslateOptionsFree(psOptions);
     796           0 :                 return nullptr;
     797             :             }
     798             : 
     799         593 :             if (psOptions->anBandList[i] != i + 1)
     800         102 :                 bAllBandsInOrder = FALSE;
     801             :         }
     802             : 
     803         243 :         if (psOptions->nBandCount != poSrcDS->GetRasterCount())
     804         191 :             bAllBandsInOrder = FALSE;
     805             :     }
     806             : 
     807        2689 :     if (static_cast<int>(psOptions->asScaleParams.size()) >
     808        2689 :         psOptions->nBandCount)
     809             :     {
     810           0 :         if (!psOptions->bHasUsedExplicitScaleBand)
     811           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     812             :                      "-scale has been specified more times than the number of "
     813             :                      "output bands");
     814             :         else
     815           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     816             :                      "-scale_XX has been specified with XX greater than the "
     817             :                      "number of output bands");
     818           0 :         if (pbUsageError)
     819           0 :             *pbUsageError = TRUE;
     820           0 :         GDALTranslateOptionsFree(psOptions);
     821           0 :         return nullptr;
     822             :     }
     823             : 
     824        2689 :     if (static_cast<int>(psOptions->adfExponent.size()) > psOptions->nBandCount)
     825             :     {
     826           0 :         if (!psOptions->bHasUsedExplicitExponentBand)
     827           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     828             :                      "-exponent has been specified more times than the number "
     829             :                      "of output bands");
     830             :         else
     831           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     832             :                      "-exponent_XX has been specified with XX greater than the "
     833             :                      "number of output bands");
     834           0 :         if (pbUsageError)
     835           0 :             *pbUsageError = TRUE;
     836           0 :         GDALTranslateOptionsFree(psOptions);
     837           0 :         return nullptr;
     838             :     }
     839             : 
     840        2689 :     if (!psOptions->bQuiet && (psOptions->bSetScale || psOptions->bSetOffset) &&
     841           7 :         psOptions->bUnscale)
     842             :     {
     843             :         // Cf https://github.com/OSGeo/gdal/issues/7863
     844           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     845             :                  "-a_scale/-a_offset are not applied by -unscale, but are set "
     846             :                  "after it, and -unscale uses the original source band "
     847             :                  "scale/offset values. "
     848             :                  "You may want to use -scale 0 1 %.16g %.16g instead. "
     849             :                  "This warning will not appear if -q is specified.",
     850           1 :                  psOptions->dfOffset, psOptions->dfOffset + psOptions->dfScale);
     851             :     }
     852             : 
     853             :     /* -------------------------------------------------------------------- */
     854             :     /*      Compute the source window from the projected source window      */
     855             :     /*      if the projected coordinates were provided.  Note that the      */
     856             :     /*      projected coordinates are in ulx, uly, lrx, lry format,         */
     857             :     /*      while the adfSrcWin is xoff, yoff, xsize, ysize with the        */
     858             :     /*      xoff,yoff being the ulx, uly in pixel/line.                     */
     859             :     /* -------------------------------------------------------------------- */
     860        2689 :     const char *pszProjection = nullptr;
     861             : 
     862        2689 :     if (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
     863        2500 :         psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
     864             :     {
     865             :         double adfGeoTransform[6];
     866             : 
     867         189 :         poSrcDS->GetGeoTransform(adfGeoTransform);
     868             : 
     869         189 :         if (adfGeoTransform[1] == 0.0 || adfGeoTransform[5] == 0.0)
     870             :         {
     871           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     872             :                      "The -projwin option was used, but the geotransform is "
     873             :                      "invalid.");
     874           0 :             GDALTranslateOptionsFree(psOptions);
     875           1 :             return nullptr;
     876             :         }
     877         189 :         if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0)
     878             :         {
     879           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     880             :                      "The -projwin option was used, but the geotransform is\n"
     881             :                      "rotated.  This configuration is not supported.");
     882           1 :             GDALTranslateOptionsFree(psOptions);
     883           1 :             return nullptr;
     884             :         }
     885             : 
     886         188 :         if (!osProjSRS.empty())
     887             :         {
     888           7 :             pszProjection = poSrcDS->GetProjectionRef();
     889           7 :             if (pszProjection != nullptr && strlen(pszProjection) > 0)
     890             :             {
     891           6 :                 OGRSpatialReference oSRSIn;
     892           6 :                 OGRSpatialReference oSRSDS;
     893           6 :                 oSRSIn.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     894           6 :                 oSRSDS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     895           6 :                 oSRSIn.SetFromUserInput(osProjSRS);
     896           6 :                 oSRSDS.SetFromUserInput(pszProjection);
     897           6 :                 if (!oSRSIn.IsSame(&oSRSDS))
     898             :                 {
     899             :                     OGRCoordinateTransformation *poCT =
     900           5 :                         OGRCreateCoordinateTransformation(&oSRSIn, &oSRSDS);
     901          10 :                     if (!(poCT &&
     902           5 :                           poCT->TransformBounds(
     903             :                               psOptions->dfULX, psOptions->dfLRY,
     904             :                               psOptions->dfLRX, psOptions->dfULY,
     905             :                               &psOptions->dfULX, &psOptions->dfLRY,
     906           5 :                               &psOptions->dfLRX, &psOptions->dfULY, 21)))
     907             :                     {
     908           0 :                         OGRCoordinateTransformation::DestroyCT(poCT);
     909             : 
     910           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     911             :                                  "-projwin_srs ignored since coordinate "
     912             :                                  "transformation failed.");
     913           0 :                         GDALTranslateOptionsFree(psOptions);
     914           0 :                         return nullptr;
     915             :                     }
     916           5 :                     delete poCT;
     917           6 :                 }
     918             :             }
     919             :             else
     920             :             {
     921           1 :                 CPLError(CE_Warning, CPLE_None,
     922             :                          "-projwin_srs ignored since the dataset has no "
     923             :                          "projection.");
     924             :             }
     925             :         }
     926             : 
     927             :         bool bAlignToInputPixels =
     928         228 :             psOptions->osResampling.empty() ||
     929          40 :             EQUALN(psOptions->osResampling.c_str(), "NEAR", 4);
     930             : 
     931         188 :         double dfULX = psOptions->dfULX;
     932         188 :         double dfULY = psOptions->dfULY;
     933             : 
     934         188 :         psOptions->srcWin.dfXOff =
     935         188 :             (dfULX - adfGeoTransform[0]) / adfGeoTransform[1];
     936         188 :         psOptions->srcWin.dfYOff =
     937         188 :             (dfULY - adfGeoTransform[3]) / adfGeoTransform[5];
     938             : 
     939             :         // In case of nearest resampling, round to integer pixels (#6610)
     940         188 :         if (bAlignToInputPixels)
     941             :         {
     942         153 :             psOptions->srcWin.dfXOff =
     943         153 :                 std::floor(psOptions->srcWin.dfXOff + 0.001);  // xoff
     944         153 :             psOptions->srcWin.dfYOff =
     945         153 :                 std::floor(psOptions->srcWin.dfYOff + 0.001);  // yoff
     946             : 
     947         153 :             dfULX = psOptions->srcWin.dfXOff * adfGeoTransform[1] +
     948         153 :                     adfGeoTransform[0];
     949         153 :             dfULY = psOptions->srcWin.dfYOff * adfGeoTransform[5] +
     950         153 :                     adfGeoTransform[3];
     951             :         }
     952             : 
     953             :         // Calculate xsize and ysize based on the (possibly snapped) ULX, ULY
     954         188 :         psOptions->srcWin.dfXSize =
     955         188 :             (psOptions->dfLRX - dfULX) / adfGeoTransform[1];  // xsize
     956         188 :         psOptions->srcWin.dfYSize =
     957         188 :             (psOptions->dfLRY - dfULY) / adfGeoTransform[5];  // ysize
     958             : 
     959         188 :         if (bAlignToInputPixels)
     960             :         {
     961         153 :             psOptions->srcWin.dfXSize =
     962         153 :                 std::ceil(psOptions->srcWin.dfXSize - 0.001);
     963         153 :             psOptions->srcWin.dfYSize =
     964         153 :                 std::ceil(psOptions->srcWin.dfYSize - 0.001);
     965             :         }
     966             : 
     967             :         /*if( !bQuiet )
     968             :             fprintf( stdout,
     969             :                      "Computed -srcwin %g %g %g %g from projected window.\n",
     970             :                      srcWin.dfXOff,
     971             :                      srcWin.dfYOff,
     972             :                      srcWin.dfXSize,
     973             :                      srcWin.dfYSize ); */
     974             :     }
     975             : 
     976             :     /* -------------------------------------------------------------------- */
     977             :     /*      Verify source window dimensions.                                */
     978             :     /* -------------------------------------------------------------------- */
     979        5375 :     if (poSrcDS->GetRasterXSize() != 0 && poSrcDS->GetRasterYSize() != 0 &&
     980        2687 :         (psOptions->srcWin.dfXSize <= 0 || psOptions->srcWin.dfYSize <= 0))
     981             :     {
     982           1 :         CPLError(
     983             :             CE_Failure, CPLE_AppDefined,
     984             :             "Error: %s-srcwin %g %g %g %g has negative width and/or height.",
     985           1 :             (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
     986           0 :              psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
     987             :                 ? "Computed "
     988             :                 : "",
     989             :             psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
     990             :             psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize);
     991           1 :         GDALTranslateOptionsFree(psOptions);
     992           1 :         return nullptr;
     993             :     }
     994             : 
     995             :     /* -------------------------------------------------------------------- */
     996             :     /*      Verify source window dimensions.                                */
     997             :     /* -------------------------------------------------------------------- */
     998        2671 :     else if (psOptions->srcWin.dfXOff <= -1 || psOptions->srcWin.dfYOff <= -1 ||
     999        5342 :              psOptions->srcWin.dfXOff + psOptions->srcWin.dfXSize >=
    1000        8010 :                  poSrcDS->GetRasterXSize() + 1 ||
    1001        5304 :              psOptions->srcWin.dfYOff + psOptions->srcWin.dfYSize >=
    1002        2652 :                  poSrcDS->GetRasterYSize() + 1)
    1003             :     {
    1004             :         const bool bCompletelyOutside =
    1005          72 :             psOptions->srcWin.dfXOff + psOptions->srcWin.dfXSize <= 0 ||
    1006          33 :             psOptions->srcWin.dfYOff + psOptions->srcWin.dfYSize <= 0 ||
    1007         104 :             psOptions->srcWin.dfXOff >= poSrcDS->GetRasterXSize() ||
    1008          32 :             psOptions->srcWin.dfYOff >= poSrcDS->GetRasterYSize();
    1009          39 :         const bool bIsError =
    1010          43 :             psOptions->bErrorOnPartiallyOutside ||
    1011           4 :             (bCompletelyOutside && psOptions->bErrorOnCompletelyOutside);
    1012          39 :         if (!psOptions->bQuiet || bIsError)
    1013             :         {
    1014          22 :             CPLErr eErr = bIsError ? CE_Failure : CE_Warning;
    1015             : 
    1016          44 :             CPLError(eErr, CPLE_AppDefined,
    1017             :                      "%s-srcwin %g %g %g %g falls %s outside source raster "
    1018             :                      "extent.%s",
    1019          22 :                      (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
    1020           9 :                       psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
    1021             :                          ? "Computed "
    1022             :                          : "",
    1023             :                      psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
    1024             :                      psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize,
    1025             :                      bCompletelyOutside ? "completely" : "partially",
    1026             :                      bIsError
    1027             :                          ? ""
    1028             :                          : " Pixels outside the source raster extent will be "
    1029             :                            "set to the NoData value (if defined), or zero.");
    1030             :         }
    1031          39 :         if (bIsError)
    1032             :         {
    1033           9 :             GDALTranslateOptionsFree(psOptions);
    1034           9 :             return nullptr;
    1035             :         }
    1036             :     }
    1037             : 
    1038             :     /* -------------------------------------------------------------------- */
    1039             :     /*      Find the output driver.                                         */
    1040             :     /* -------------------------------------------------------------------- */
    1041        2678 :     if (psOptions->osFormat.empty())
    1042             :     {
    1043        1371 :         const std::string osFormat = GetOutputDriverForRaster(pszDest);
    1044        1371 :         if (osFormat.empty())
    1045             :         {
    1046           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1047             :                      "Could not identify an output driver for %s", pszDest);
    1048           1 :             GDALTranslateOptionsFree(psOptions);
    1049           1 :             return nullptr;
    1050             :         }
    1051        1370 :         psOptions->osFormat = osFormat;
    1052             :     }
    1053             : 
    1054        2677 :     GDALDriverH hDriver = GDALGetDriverByName(psOptions->osFormat.c_str());
    1055        2677 :     if (hDriver == nullptr)
    1056             :     {
    1057           1 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1058             :                  "Output driver `%s' not recognised.",
    1059             :                  psOptions->osFormat.c_str());
    1060           1 :         GDALTranslateOptionsFree(psOptions);
    1061           1 :         return nullptr;
    1062             :     }
    1063             : 
    1064             :     /* -------------------------------------------------------------------- */
    1065             :     /*      Make sure we cleanup if there is an existing dataset of this    */
    1066             :     /*      name.  But even if that seems to fail we will continue since    */
    1067             :     /*      it might just be a corrupt file or something.                   */
    1068             :     /*      This is needed for                                              */
    1069             :     /*      gdal_translate foo.tif foo.tif.ovr -outsize 50% 50%             */
    1070             :     /* -------------------------------------------------------------------- */
    1071        2676 :     if (psOptions->aosCreateOptions.FetchBool("APPEND_SUBDATASET", false))
    1072             :     {
    1073           7 :         if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE_SUBDATASETS,
    1074           7 :                                 nullptr) == nullptr)
    1075             :         {
    1076           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1077             :                      "Subdataset creation not supported for driver %s",
    1078             :                      GDALGetDescription(hDriver));
    1079           1 :             GDALTranslateOptionsFree(psOptions);
    1080           1 :             return nullptr;
    1081             :         }
    1082             :     }
    1083             :     else
    1084             :     {
    1085        2669 :         if (!EQUAL(psOptions->osFormat.c_str(), "VRT"))
    1086             :         {
    1087             :             // Prevent GDALDriver::CreateCopy() from doing that again.
    1088             :             psOptions->aosCreateOptions.SetNameValue(
    1089        2229 :                 "@QUIET_DELETE_ON_CREATE_COPY", "NO");
    1090             :         }
    1091             : 
    1092        2669 :         if (psOptions->bNoOverwrite && !EQUAL(pszDest, ""))
    1093             :         {
    1094             :             VSIStatBufL sStat;
    1095          72 :             if (VSIStatL(pszDest, &sStat) == 0)
    1096             :             {
    1097           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1098             :                          "File '%s' already exists. Specify the --overwrite "
    1099             :                          "option to overwrite it.",
    1100             :                          pszDest);
    1101           0 :                 GDALTranslateOptionsFree(psOptions);
    1102           0 :                 return nullptr;
    1103             :             }
    1104          72 :             else if (std::unique_ptr<GDALDataset>(GDALDataset::Open(pszDest)))
    1105             :             {
    1106           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1107             :                          "Dataset '%s' already exists. Specify the --overwrite "
    1108             :                          "option to overwrite it.",
    1109             :                          pszDest);
    1110           0 :                 GDALTranslateOptionsFree(psOptions);
    1111           0 :                 return nullptr;
    1112             :             }
    1113             :         }
    1114             : 
    1115        2669 :         GDALDriver::FromHandle(hDriver)->QuietDeleteForCreateCopy(pszDest,
    1116             :                                                                   poSrcDS);
    1117             : 
    1118             :         // Make sure to load early overviews, so that on the GTiff driver
    1119             :         // external .ovr is looked for before it might be created as the
    1120             :         // output dataset !
    1121        2669 :         if (poSrcDS->GetRasterCount())
    1122             :         {
    1123        2666 :             auto poBand = poSrcDS->GetRasterBand(1);
    1124        2666 :             if (poBand)
    1125        2666 :                 poBand->GetOverviewCount();
    1126             :         }
    1127             :     }
    1128             : 
    1129        2675 :     char **papszDriverMD = GDALGetMetadata(hDriver, nullptr);
    1130             : 
    1131        2675 :     if (!CPLTestBool(
    1132             :             CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER, "FALSE")))
    1133             :     {
    1134           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1135             :                  "%s driver has no raster capabilities.",
    1136             :                  psOptions->osFormat.c_str());
    1137           1 :         GDALTranslateOptionsFree(psOptions);
    1138           1 :         return nullptr;
    1139             :     }
    1140             : 
    1141        2674 :     if (!CPLTestBool(
    1142        2851 :             CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")) &&
    1143         177 :         !CPLTestBool(
    1144             :             CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATECOPY, "FALSE")))
    1145             :     {
    1146           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1147             :                  "%s driver has no creation capabilities.",
    1148             :                  psOptions->osFormat.c_str());
    1149           1 :         GDALTranslateOptionsFree(psOptions);
    1150           1 :         return nullptr;
    1151             :     }
    1152             : 
    1153             :     /* -------------------------------------------------------------------- */
    1154             :     /*      The short form is to CreateCopy().  We use this if the input    */
    1155             :     /*      matches the whole dataset.  Eventually we should rewrite        */
    1156             :     /*      this entire program to use virtual datasets to construct a      */
    1157             :     /*      virtual input source to copy from.                              */
    1158             :     /* -------------------------------------------------------------------- */
    1159             : 
    1160        2673 :     const bool bKeepResolution =
    1161        2482 :         psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
    1162        7605 :         psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0 &&
    1163        2450 :         psOptions->dfXRes == 0.0;
    1164             :     const bool bSpatialArrangementPreserved =
    1165        1794 :         psOptions->srcWin.dfXOff == 0 && psOptions->srcWin.dfYOff == 0 &&
    1166        1723 :         psOptions->srcWin.dfXSize == poSrcDS->GetRasterXSize() &&
    1167        4467 :         psOptions->srcWin.dfYSize == poSrcDS->GetRasterYSize() &&
    1168        2673 :         bKeepResolution;
    1169             : 
    1170        2414 :     if (psOptions->eOutputType == GDT_Unknown &&
    1171        4781 :         psOptions->asScaleParams.empty() && psOptions->adfExponent.empty() &&
    1172        2366 :         !psOptions->bUnscale && !psOptions->bSetScale &&
    1173        2361 :         !psOptions->bSetOffset && psOptions->aosMetadataOptions.empty() &&
    1174        2335 :         psOptions->aosDomainMetadataOptions.empty() && bAllBandsInOrder &&
    1175        2254 :         psOptions->eMaskMode == MASK_AUTO && bSpatialArrangementPreserved &&
    1176        1031 :         !psOptions->bNoGCP && psOptions->nGCPCount == 0 && !bGotBounds &&
    1177        1004 :         !bGotGeoTransform && psOptions->osOutputSRS.empty() &&
    1178         952 :         psOptions->dfOutputCoordinateEpoch == 0 && !psOptions->bSetNoData &&
    1179         927 :         !psOptions->bUnsetNoData && psOptions->nRGBExpand == 0 &&
    1180         903 :         !psOptions->bNoRAT && psOptions->anColorInterp.empty() &&
    1181        5087 :         !psOptions->bNoXMP && psOptions->nOvLevel == OVR_LEVEL_AUTO)
    1182             :     {
    1183             : 
    1184             :         // For gdal_translate_fuzzer
    1185         887 :         if (psOptions->nLimitOutSize > 0)
    1186             :         {
    1187             :             vsi_l_offset nRawOutSize =
    1188           0 :                 static_cast<vsi_l_offset>(poSrcDS->GetRasterXSize()) *
    1189           0 :                 poSrcDS->GetRasterYSize() * psOptions->nBandCount;
    1190           0 :             if (psOptions->nBandCount)
    1191             :             {
    1192           0 :                 nRawOutSize *= GDALGetDataTypeSizeBytes(
    1193             :                     poSrcDS->GetRasterBand(1)->GetRasterDataType());
    1194             :             }
    1195           0 :             if (nRawOutSize >
    1196           0 :                 static_cast<vsi_l_offset>(psOptions->nLimitOutSize))
    1197             :             {
    1198           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1199             :                          "Attempt to create %dx%d dataset is above authorized "
    1200             :                          "limit.",
    1201             :                          poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
    1202           0 :                 GDALTranslateOptionsFree(psOptions);
    1203           0 :                 return nullptr;
    1204             :             }
    1205             :         }
    1206             : 
    1207             :         /* --------------------------------------------------------------------
    1208             :          */
    1209             :         /*      Compute stats if required. */
    1210             :         /* --------------------------------------------------------------------
    1211             :          */
    1212             : 
    1213         887 :         if (psOptions->bStats && EQUAL(psOptions->osFormat.c_str(), "COG"))
    1214             :         {
    1215           2 :             psOptions->aosCreateOptions.SetNameValue("STATISTICS", "YES");
    1216             :         }
    1217         885 :         else if (psOptions->bStats)
    1218             :         {
    1219           2 :             for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
    1220             :             {
    1221             :                 double dfMin, dfMax, dfMean, dfStdDev;
    1222           1 :                 poSrcDS->GetRasterBand(i + 1)->ComputeStatistics(
    1223           1 :                     psOptions->bApproxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
    1224           1 :                     GDALDummyProgress, nullptr);
    1225             :             }
    1226             :         }
    1227             : 
    1228         887 :         hOutDS = GDALCreateCopy(
    1229             :             hDriver, pszDest, GDALDataset::ToHandle(poSrcDS),
    1230         887 :             psOptions->bStrict, psOptions->aosCreateOptions.List(),
    1231             :             psOptions->pfnProgress, psOptions->pProgressData);
    1232         887 :         hOutDS = GDALTranslateFlush(hOutDS);
    1233             : 
    1234         887 :         GDALTranslateOptionsFree(psOptions);
    1235         887 :         return hOutDS;
    1236             :     }
    1237             : 
    1238        1786 :     if (psOptions->aosCreateOptions.FetchNameValue("COPY_SRC_OVERVIEWS"))
    1239             :     {
    1240           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1241             :                  "General options of gdal_translate make the "
    1242             :                  "COPY_SRC_OVERVIEWS creation option ineffective as they hide "
    1243             :                  "the overviews");
    1244             :     }
    1245             : 
    1246             :     /* -------------------------------------------------------------------- */
    1247             :     /*      Establish some parameters.                                      */
    1248             :     /* -------------------------------------------------------------------- */
    1249        1786 :     int nOXSize = 0;
    1250        1786 :     int nOYSize = 0;
    1251             : 
    1252        1786 :     bool bHasSrcGeoTransform = false;
    1253        1786 :     double adfSrcGeoTransform[6] = {};
    1254        1786 :     if (poSrcDS->GetGeoTransform(adfSrcGeoTransform) == CE_None)
    1255        1556 :         bHasSrcGeoTransform = true;
    1256             : 
    1257        1786 :     const bool bOutsizeExplicitlySet =
    1258        3351 :         !(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
    1259        1565 :           psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0);
    1260        1786 :     if (psOptions->dfXRes != 0.0 && psOptions->dfYRes != 0.0)
    1261             :     {
    1262          12 :         if (!(bHasSrcGeoTransform && psOptions->nGCPCount == 0 &&
    1263          12 :               adfSrcGeoTransform[2] == 0.0 && adfSrcGeoTransform[4] == 0.0))
    1264             :         {
    1265           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1266             :                      "The -tr option was used, but there's no geotransform or "
    1267             :                      "it is\n"
    1268             :                      "rotated.  This configuration is not supported.");
    1269           0 :             GDALTranslateOptionsFree(psOptions);
    1270           0 :             return nullptr;
    1271             :         }
    1272          12 :         const double dfOXSize = psOptions->srcWin.dfXSize / psOptions->dfXRes *
    1273          12 :                                     adfSrcGeoTransform[1] +
    1274             :                                 0.5;
    1275          12 :         const double dfOYSize = psOptions->srcWin.dfYSize / psOptions->dfYRes *
    1276          12 :                                     fabs(adfSrcGeoTransform[5]) +
    1277             :                                 0.5;
    1278          12 :         if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
    1279          24 :             dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
    1280             :         {
    1281           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1282             :                      "Invalid output size: %g x %g", dfOXSize, dfOYSize);
    1283           0 :             GDALTranslateOptionsFree(psOptions);
    1284           0 :             return nullptr;
    1285             :         }
    1286          12 :         nOXSize = static_cast<int>(dfOXSize);
    1287          12 :         nOYSize = static_cast<int>(dfOYSize);
    1288             :     }
    1289        1774 :     else if (!bOutsizeExplicitlySet)
    1290             :     {
    1291        1551 :         double dfOXSize = ceil(psOptions->srcWin.dfXSize - 0.001);
    1292        1551 :         double dfOYSize = ceil(psOptions->srcWin.dfYSize - 0.001);
    1293        1551 :         if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
    1294        3102 :             dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
    1295             :         {
    1296           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1297             :                      "Invalid output size: %g x %g", dfOXSize, dfOYSize);
    1298           2 :             GDALTranslateOptionsFree(psOptions);
    1299           2 :             return nullptr;
    1300             :         }
    1301        1549 :         nOXSize = static_cast<int>(dfOXSize);
    1302        1549 :         nOYSize = static_cast<int>(dfOYSize);
    1303             :     }
    1304             :     else
    1305             :     {
    1306         223 :         if (!(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0))
    1307             :         {
    1308         221 :             if (psOptions->nOXSizePixel != 0)
    1309         191 :                 nOXSize = psOptions->nOXSizePixel;
    1310             :             else
    1311             :             {
    1312          30 :                 const double dfOXSize =
    1313          30 :                     psOptions->dfOXSizePct / 100 * psOptions->srcWin.dfXSize;
    1314          30 :                 if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize))
    1315             :                 {
    1316           1 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1317             :                              "Invalid output width: %g", dfOXSize);
    1318           1 :                     GDALTranslateOptionsFree(psOptions);
    1319           1 :                     return nullptr;
    1320             :                 }
    1321          29 :                 nOXSize = static_cast<int>(dfOXSize);
    1322             :             }
    1323             :         }
    1324             : 
    1325         222 :         if (!(psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0))
    1326             :         {
    1327         161 :             if (psOptions->nOYSizePixel != 0)
    1328         133 :                 nOYSize = psOptions->nOYSizePixel;
    1329             :             else
    1330             :             {
    1331          28 :                 const double dfOYSize =
    1332          28 :                     psOptions->dfOYSizePct / 100 * psOptions->srcWin.dfYSize;
    1333          28 :                 if (dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
    1334             :                 {
    1335           1 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1336             :                              "Invalid output height: %g", dfOYSize);
    1337           1 :                     GDALTranslateOptionsFree(psOptions);
    1338           1 :                     return nullptr;
    1339             :                 }
    1340          27 :                 nOYSize = static_cast<int>(dfOYSize);
    1341             :             }
    1342             :         }
    1343             : 
    1344         221 :         if (psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0)
    1345             :         {
    1346           2 :             const double dfOXSize = static_cast<double>(nOYSize) *
    1347           2 :                                         psOptions->srcWin.dfXSize /
    1348           2 :                                         psOptions->srcWin.dfYSize +
    1349             :                                     0.5;
    1350           2 :             if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize))
    1351             :             {
    1352           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1353             :                          "Invalid output width: %g", dfOXSize);
    1354           0 :                 GDALTranslateOptionsFree(psOptions);
    1355           0 :                 return nullptr;
    1356             :             }
    1357           2 :             nOXSize = static_cast<int>(dfOXSize);
    1358             :         }
    1359         219 :         else if (psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0)
    1360             :         {
    1361          61 :             const double dfOYSize = static_cast<double>(nOXSize) *
    1362          61 :                                         psOptions->srcWin.dfYSize /
    1363          61 :                                         psOptions->srcWin.dfXSize +
    1364             :                                     0.5;
    1365          61 :             if (dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
    1366             :             {
    1367           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1368             :                          "Invalid output height: %g", dfOYSize);
    1369           0 :                 GDALTranslateOptionsFree(psOptions);
    1370           0 :                 return nullptr;
    1371             :             }
    1372          61 :             nOYSize = static_cast<int>(dfOYSize);
    1373             :         }
    1374             :     }
    1375             : 
    1376        1782 :     if (nOXSize <= 0 || nOYSize <= 0)
    1377             :     {
    1378           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1379             :                  "Attempt to create %dx%d dataset is illegal.", nOXSize,
    1380             :                  nOYSize);
    1381           0 :         GDALTranslateOptionsFree(psOptions);
    1382           0 :         return nullptr;
    1383             :     }
    1384             : 
    1385             :     // Build overview dataset if -ovr is specified
    1386        1782 :     GDALDataset *poSrcOvrDS = nullptr;
    1387        1782 :     GDALDataset *poSrcDSOri = poSrcDS;
    1388        1782 :     const auto poFirstBand = poSrcDS->GetRasterBand(1);
    1389        1782 :     const int nOvCount = poFirstBand ? poFirstBand->GetOverviewCount() : 0;
    1390        1782 :     if (psOptions->nOvLevel < OVR_LEVEL_AUTO && poFirstBand && nOvCount > 0)
    1391             :     {
    1392           4 :         int iOvr = 0;
    1393           7 :         for (; iOvr < nOvCount - 1; iOvr++)
    1394             :         {
    1395           4 :             if (poFirstBand->GetOverview(iOvr)->GetXSize() <= nOXSize)
    1396             :             {
    1397           1 :                 break;
    1398             :             }
    1399             :         }
    1400           4 :         iOvr += (psOptions->nOvLevel - OVR_LEVEL_AUTO);
    1401           4 :         if (iOvr >= 0)
    1402             :         {
    1403           3 :             CPLDebug("GDAL", "Selecting overview level %d", iOvr);
    1404           3 :             poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, iOvr,
    1405             :                                                    /* bThisLevelOnly = */ true);
    1406           4 :         }
    1407             :     }
    1408        1778 :     else if (psOptions->nOvLevel >= OVR_LEVEL_NONE)
    1409             :     {
    1410          11 :         poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, psOptions->nOvLevel,
    1411             :                                                /* bThisLevelOnly = */ true);
    1412          11 :         if (poSrcOvrDS == nullptr)
    1413             :         {
    1414           3 :             if (!psOptions->bQuiet)
    1415             :             {
    1416           3 :                 if (nOvCount > 0)
    1417             :                 {
    1418           2 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1419             :                              "Cannot get overview level %d. "
    1420             :                              "Defaulting to level %d.",
    1421             :                              psOptions->nOvLevel, nOvCount - 1);
    1422             :                 }
    1423             :                 else
    1424             :                 {
    1425           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1426             :                              "Cannot get overview level %d. "
    1427             :                              "Defaulting to full resolution.",
    1428             :                              psOptions->nOvLevel);
    1429             :                 }
    1430             :             }
    1431           3 :             if (nOvCount > 0)
    1432             :                 poSrcOvrDS =
    1433           2 :                     GDALCreateOverviewDataset(poSrcDS, nOvCount - 1,
    1434             :                                               /* bThisLevelOnly = */ true);
    1435             :         }
    1436          11 :         if (poSrcOvrDS && psOptions->dfXRes == 0.0 && !bOutsizeExplicitlySet)
    1437             :         {
    1438             :             const double dfRatioX =
    1439           8 :                 static_cast<double>(poSrcDSOri->GetRasterXSize()) /
    1440           8 :                 poSrcOvrDS->GetRasterXSize();
    1441             :             const double dfRatioY =
    1442           8 :                 static_cast<double>(poSrcDSOri->GetRasterYSize()) /
    1443           8 :                 poSrcOvrDS->GetRasterYSize();
    1444           8 :             nOXSize =
    1445           8 :                 std::max(1, static_cast<int>(ceil(nOXSize / dfRatioX - 0.001)));
    1446           8 :             nOYSize =
    1447           8 :                 std::max(1, static_cast<int>(ceil(nOYSize / dfRatioY - 0.001)));
    1448             :         }
    1449             :     }
    1450             : 
    1451        1782 :     if (poSrcOvrDS)
    1452          13 :         poSrcDS = poSrcOvrDS;
    1453             :     else
    1454        1769 :         poSrcDS->Reference();
    1455             : 
    1456             :     // For gdal_translate_fuzzer
    1457        1782 :     if (psOptions->nLimitOutSize > 0)
    1458             :     {
    1459           0 :         vsi_l_offset nRawOutSize = static_cast<vsi_l_offset>(nOXSize) * nOYSize;
    1460           0 :         if (psOptions->nBandCount)
    1461             :         {
    1462           0 :             if (nRawOutSize > std::numeric_limits<vsi_l_offset>::max() /
    1463           0 :                                   psOptions->nBandCount)
    1464             :             {
    1465           0 :                 GDALTranslateOptionsFree(psOptions);
    1466           0 :                 return nullptr;
    1467             :             }
    1468           0 :             nRawOutSize *= psOptions->nBandCount;
    1469           0 :             const int nDTSize = GDALGetDataTypeSizeBytes(
    1470             :                 poSrcDS->GetRasterBand(1)->GetRasterDataType());
    1471           0 :             if (nDTSize > 0 &&
    1472             :                 nRawOutSize >
    1473           0 :                     std::numeric_limits<vsi_l_offset>::max() / nDTSize)
    1474             :             {
    1475           0 :                 GDALTranslateOptionsFree(psOptions);
    1476           0 :                 poSrcDS->Release();
    1477           0 :                 return nullptr;
    1478             :             }
    1479           0 :             nRawOutSize *= nDTSize;
    1480             :         }
    1481           0 :         if (nRawOutSize > static_cast<vsi_l_offset>(psOptions->nLimitOutSize))
    1482             :         {
    1483           0 :             CPLError(
    1484             :                 CE_Failure, CPLE_IllegalArg,
    1485             :                 "Attempt to create %dx%d dataset is above authorized limit.",
    1486             :                 nOXSize, nOYSize);
    1487           0 :             GDALTranslateOptionsFree(psOptions);
    1488           0 :             poSrcDS->Release();
    1489           0 :             return nullptr;
    1490             :         }
    1491             :     }
    1492             : 
    1493             :     /* ==================================================================== */
    1494             :     /*      Create a virtual dataset.                                       */
    1495             :     /* ==================================================================== */
    1496             : 
    1497             :     /* -------------------------------------------------------------------- */
    1498             :     /*      Make a virtual clone.                                           */
    1499             :     /* -------------------------------------------------------------------- */
    1500        1782 :     VRTDataset *poVDS = static_cast<VRTDataset *>(VRTCreate(nOXSize, nOYSize));
    1501             : 
    1502        1782 :     if (psOptions->nGCPCount == 0)
    1503             :     {
    1504        3546 :         if (psOptions->osOutputSRS == "null" ||
    1505        1773 :             psOptions->osOutputSRS == "none")
    1506             :         {
    1507           2 :             poVDS->SetSpatialRef(nullptr);
    1508             :         }
    1509             :         else
    1510             :         {
    1511        3542 :             OGRSpatialReference oSRS;
    1512        1771 :             if (!psOptions->osOutputSRS.empty())
    1513             :             {
    1514         106 :                 oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
    1515         106 :                 oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1516             :             }
    1517             :             else
    1518             :             {
    1519        1665 :                 const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
    1520        1665 :                 if (poSrcSRS)
    1521        1344 :                     oSRS = *poSrcSRS;
    1522             :             }
    1523        1771 :             if (!oSRS.IsEmpty())
    1524             :             {
    1525        1450 :                 if (psOptions->dfOutputCoordinateEpoch > 0)
    1526           4 :                     oSRS.SetCoordinateEpoch(psOptions->dfOutputCoordinateEpoch);
    1527        1450 :                 poVDS->SetSpatialRef(&oSRS);
    1528             :             }
    1529             :         }
    1530             :     }
    1531             : 
    1532        1782 :     bool bHasDstGeoTransform = false;
    1533        1782 :     double adfDstGeoTransform[6] = {};
    1534             : 
    1535        1782 :     if (bGotBounds)
    1536             :     {
    1537          20 :         bHasDstGeoTransform = true;
    1538          20 :         adfDstGeoTransform[0] = psOptions->adfULLR[0];
    1539          20 :         adfDstGeoTransform[1] =
    1540          20 :             (psOptions->adfULLR[2] - psOptions->adfULLR[0]) / nOXSize;
    1541          20 :         adfDstGeoTransform[2] = 0.0;
    1542          20 :         adfDstGeoTransform[3] = psOptions->adfULLR[1];
    1543          20 :         adfDstGeoTransform[4] = 0.0;
    1544          20 :         adfDstGeoTransform[5] =
    1545          20 :             (psOptions->adfULLR[3] - psOptions->adfULLR[1]) / nOYSize;
    1546             : 
    1547          20 :         poVDS->SetGeoTransform(adfDstGeoTransform);
    1548             :     }
    1549             : 
    1550        1762 :     else if (bGotGeoTransform)
    1551             :     {
    1552           3 :         bHasDstGeoTransform = true;
    1553          21 :         for (int i = 0; i < 6; i++)
    1554          18 :             adfDstGeoTransform[i] = psOptions->adfGT[i];
    1555           3 :         poVDS->SetGeoTransform(adfDstGeoTransform);
    1556             :     }
    1557             : 
    1558        1759 :     else if (bHasSrcGeoTransform && psOptions->nGCPCount == 0)
    1559             :     {
    1560        1527 :         bHasDstGeoTransform = true;
    1561        1527 :         memcpy(adfDstGeoTransform, adfSrcGeoTransform, 6 * sizeof(double));
    1562        1527 :         adfDstGeoTransform[0] +=
    1563        1527 :             psOptions->srcWin.dfXOff * adfDstGeoTransform[1] +
    1564        1527 :             psOptions->srcWin.dfYOff * adfDstGeoTransform[2];
    1565        1527 :         adfDstGeoTransform[3] +=
    1566        1527 :             psOptions->srcWin.dfXOff * adfDstGeoTransform[4] +
    1567        1527 :             psOptions->srcWin.dfYOff * adfDstGeoTransform[5];
    1568             : 
    1569        1527 :         const double dfX = static_cast<double>(nOXSize);
    1570        1527 :         const double dfY = static_cast<double>(nOYSize);
    1571        1527 :         adfDstGeoTransform[1] *= psOptions->srcWin.dfXSize / dfX;
    1572        1527 :         adfDstGeoTransform[2] *= psOptions->srcWin.dfYSize / dfY;
    1573        1527 :         adfDstGeoTransform[4] *= psOptions->srcWin.dfXSize / dfX;
    1574        1527 :         adfDstGeoTransform[5] *= psOptions->srcWin.dfYSize / dfY;
    1575             : 
    1576        1527 :         if (psOptions->dfXRes != 0.0)
    1577             :         {
    1578          12 :             adfDstGeoTransform[1] = psOptions->dfXRes;
    1579          12 :             adfDstGeoTransform[5] = (adfDstGeoTransform[5] > 0)
    1580          12 :                                         ? psOptions->dfYRes
    1581          12 :                                         : -psOptions->dfYRes;
    1582             :         }
    1583             : 
    1584        1527 :         poVDS->SetGeoTransform(adfDstGeoTransform);
    1585             :     }
    1586             : 
    1587        1782 :     if (psOptions->nGCPCount != 0)
    1588             :     {
    1589          18 :         OGRSpatialReference oSRS;
    1590          18 :         if (psOptions->osOutputSRS == "null" ||
    1591           9 :             psOptions->osOutputSRS == "none")
    1592             :         {
    1593             :             // nothing to do
    1594             :         }
    1595           9 :         else if (!psOptions->osOutputSRS.empty())
    1596             :         {
    1597           5 :             oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
    1598           5 :             oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1599             :         }
    1600             :         else
    1601             :         {
    1602           4 :             const OGRSpatialReference *poSrcSRS = poSrcDS->GetGCPSpatialRef();
    1603           4 :             if (poSrcSRS)
    1604           0 :                 oSRS = *poSrcSRS;
    1605             :         }
    1606           9 :         poVDS->SetGCPs(psOptions->nGCPCount, psOptions->pasGCPs,
    1607           9 :                        !oSRS.IsEmpty() ? &oSRS : nullptr);
    1608             :     }
    1609             : 
    1610        1773 :     else if (!psOptions->bNoGCP && poSrcDSOri->GetGCPCount() > 0)
    1611             :     {
    1612           1 :         const int nGCPs = poSrcDSOri->GetGCPCount();
    1613             : 
    1614           1 :         GDAL_GCP *pasGCPs = GDALDuplicateGCPs(nGCPs, poSrcDSOri->GetGCPs());
    1615             : 
    1616           5 :         for (int i = 0; i < nGCPs; i++)
    1617             :         {
    1618           4 :             pasGCPs[i].dfGCPPixel -= psOptions->srcWin.dfXOff;
    1619           4 :             pasGCPs[i].dfGCPLine -= psOptions->srcWin.dfYOff;
    1620           4 :             pasGCPs[i].dfGCPPixel *=
    1621           4 :                 nOXSize / static_cast<double>(psOptions->srcWin.dfXSize);
    1622           4 :             pasGCPs[i].dfGCPLine *=
    1623           4 :                 nOYSize / static_cast<double>(psOptions->srcWin.dfYSize);
    1624             :         }
    1625             : 
    1626           1 :         poVDS->SetGCPs(nGCPs, pasGCPs, poSrcDSOri->GetGCPSpatialRef());
    1627             : 
    1628           1 :         GDALDeinitGCPs(nGCPs, pasGCPs);
    1629           1 :         CPLFree(pasGCPs);
    1630             :     }
    1631             : 
    1632             :     /* -------------------------------------------------------------------- */
    1633             :     /*      To make the VRT to look less awkward (but this is optional      */
    1634             :     /*      in fact), avoid negative values.                                */
    1635             :     /* -------------------------------------------------------------------- */
    1636        1782 :     GDALTranslateOptions::PixelLineWindow dstWin{
    1637        1782 :         0.0, 0.0, static_cast<double>(nOXSize), static_cast<double>(nOYSize)};
    1638             : 
    1639             :     // When specifying -tr with non-nearest resampling, make sure that the
    1640             :     // size of target window precisely matches the requested resolution, to
    1641             :     // avoid any shift.
    1642        1552 :     if (bHasSrcGeoTransform && bHasDstGeoTransform &&
    1643        3339 :         psOptions->dfXRes != 0.0 && !psOptions->osResampling.empty() &&
    1644           5 :         !EQUALN(psOptions->osResampling.c_str(), "NEAR", 4))
    1645             :     {
    1646           5 :         dstWin.dfXSize = psOptions->srcWin.dfXSize * adfSrcGeoTransform[1] /
    1647           5 :                          adfDstGeoTransform[1];
    1648           5 :         dstWin.dfYSize = psOptions->srcWin.dfYSize *
    1649           5 :                          fabs(adfSrcGeoTransform[5] / adfDstGeoTransform[5]);
    1650             :     }
    1651             : 
    1652        1782 :     GDALTranslateOptions::PixelLineWindow srcWinOri(psOptions->srcWin);
    1653             :     const double dfRatioX =
    1654        1782 :         poSrcDS->GetRasterXSize() == 0
    1655        1782 :             ? 1.0
    1656        1782 :             : static_cast<double>(poSrcDSOri->GetRasterXSize()) /
    1657        1782 :                   poSrcDS->GetRasterXSize();
    1658             :     const double dfRatioY =
    1659        1782 :         poSrcDS->GetRasterYSize() == 0
    1660        1782 :             ? 1.0
    1661        1782 :             : static_cast<double>(poSrcDSOri->GetRasterYSize()) /
    1662        1782 :                   poSrcDS->GetRasterYSize();
    1663        1782 :     psOptions->srcWin.dfXOff /= dfRatioX;
    1664        1782 :     psOptions->srcWin.dfYOff /= dfRatioY;
    1665        1782 :     psOptions->srcWin.dfXSize /= dfRatioX;
    1666        1782 :     psOptions->srcWin.dfYSize /= dfRatioY;
    1667        1782 :     FixSrcDstWindow(psOptions->srcWin, dstWin, poSrcDS->GetRasterXSize(),
    1668             :                     poSrcDS->GetRasterYSize());
    1669             : 
    1670             :     /* -------------------------------------------------------------------- */
    1671             :     /*      Transfer generally applicable metadata.                         */
    1672             :     /* -------------------------------------------------------------------- */
    1673        1782 :     char **papszMetadata = CSLDuplicate(poSrcDS->GetMetadata());
    1674        3480 :     if (!psOptions->asScaleParams.empty() || psOptions->bUnscale ||
    1675        1698 :         psOptions->eOutputType != GDT_Unknown)
    1676             :     {
    1677             :         /* Remove TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */
    1678             :         /* if the data range may change because of options */
    1679         308 :         char **papszIter = papszMetadata;
    1680         679 :         while (papszIter && *papszIter)
    1681             :         {
    1682         371 :             if (STARTS_WITH_CI(*papszIter, "TIFFTAG_MINSAMPLEVALUE=") ||
    1683         371 :                 STARTS_WITH_CI(*papszIter, "TIFFTAG_MAXSAMPLEVALUE="))
    1684             :             {
    1685           0 :                 CPLFree(*papszIter);
    1686           0 :                 memmove(papszIter, papszIter + 1,
    1687           0 :                         sizeof(char *) * (CSLCount(papszIter + 1) + 1));
    1688             :             }
    1689             :             else
    1690         371 :                 papszIter++;
    1691             :         }
    1692             :     }
    1693             : 
    1694             :     // Remove NITF_BLOCKA_ stuff if georeferencing is changed
    1695        2621 :     if (!(psOptions->srcWin.dfXOff == 0 && psOptions->srcWin.dfYOff == 0 &&
    1696         839 :           psOptions->srcWin.dfXSize == poSrcDS->GetRasterXSize() &&
    1697         751 :           psOptions->srcWin.dfYSize == poSrcDS->GetRasterYSize() &&
    1698         750 :           psOptions->nGCPCount == 0 && !bGotBounds && !bGotGeoTransform))
    1699             :     {
    1700        1064 :         char **papszIter = papszMetadata;
    1701        3024 :         while (papszIter && *papszIter)
    1702             :         {
    1703        1960 :             if (STARTS_WITH_CI(*papszIter, "NITF_BLOCKA_"))
    1704             :             {
    1705          10 :                 CPLFree(*papszIter);
    1706          10 :                 memmove(papszIter, papszIter + 1,
    1707          10 :                         sizeof(char *) * (CSLCount(papszIter + 1) + 1));
    1708             :             }
    1709             :             else
    1710        1950 :                 papszIter++;
    1711             :         }
    1712             :     }
    1713             : 
    1714             :     {
    1715        1782 :         char **papszIter = papszMetadata;
    1716        4564 :         while (papszIter && *papszIter)
    1717             :         {
    1718             :             // Do not preserve the CACHE_PATH from the WMS driver
    1719        2782 :             if (STARTS_WITH_CI(*papszIter, "CACHE_PATH="))
    1720             :             {
    1721           0 :                 CPLFree(*papszIter);
    1722           0 :                 memmove(papszIter, papszIter + 1,
    1723           0 :                         sizeof(char *) * (CSLCount(papszIter + 1) + 1));
    1724             :             }
    1725             :             else
    1726        2782 :                 papszIter++;
    1727             :         }
    1728             :     }
    1729             : 
    1730        1782 :     poVDS->SetMetadata(papszMetadata);
    1731        1782 :     CSLDestroy(papszMetadata);
    1732        1782 :     AttachMetadata(GDALDataset::ToHandle(poVDS), psOptions->aosMetadataOptions);
    1733             : 
    1734        1782 :     AttachDomainMetadata(GDALDataset::ToHandle(poVDS),
    1735        1782 :                          psOptions->aosDomainMetadataOptions);
    1736             : 
    1737             :     const char *pszInterleave =
    1738        1782 :         poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
    1739        1782 :     if (pszInterleave)
    1740        1606 :         poVDS->SetMetadataItem("INTERLEAVE", pszInterleave, "IMAGE_STRUCTURE");
    1741             : 
    1742             :     {
    1743             :         const char *pszCompression =
    1744        1782 :             poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
    1745        1782 :         if (pszCompression)
    1746             :         {
    1747          48 :             poVDS->SetMetadataItem("COMPRESSION", pszCompression,
    1748          48 :                                    "IMAGE_STRUCTURE");
    1749             :         }
    1750             :     }
    1751             : 
    1752             :     /* ISIS3 metadata preservation */
    1753        1782 :     char **papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
    1754        1782 :     if (papszMD_ISIS3 != nullptr)
    1755             :     {
    1756           3 :         if (!bAllBandsInOrder)
    1757             :         {
    1758             :             CPLString osJSON = EditISIS3MetadataForBandChange(
    1759           2 :                 papszMD_ISIS3[0], poSrcDS->GetRasterCount(), psOptions);
    1760           1 :             if (!osJSON.empty())
    1761             :             {
    1762           1 :                 char *apszMD[] = {&osJSON[0], nullptr};
    1763           1 :                 poVDS->SetMetadata(apszMD, "json:ISIS3");
    1764             :             }
    1765             :         }
    1766             :         else
    1767             :         {
    1768           2 :             poVDS->SetMetadata(papszMD_ISIS3, "json:ISIS3");
    1769             :         }
    1770             :     }
    1771             : 
    1772             :     // PDS4 -> PDS4 special case
    1773        1782 :     if (EQUAL(psOptions->osFormat.c_str(), "PDS4"))
    1774             :     {
    1775           3 :         char **papszMD_PDS4 = poSrcDS->GetMetadata("xml:PDS4");
    1776           3 :         if (papszMD_PDS4 != nullptr)
    1777           2 :             poVDS->SetMetadata(papszMD_PDS4, "xml:PDS4");
    1778             :     }
    1779             : 
    1780             :     // VICAR -> VICAR special case
    1781        1782 :     if (EQUAL(psOptions->osFormat.c_str(), "VICAR"))
    1782             :     {
    1783           0 :         char **papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
    1784           0 :         if (papszMD_VICAR != nullptr)
    1785           0 :             poVDS->SetMetadata(papszMD_VICAR, "json:VICAR");
    1786             :     }
    1787             : 
    1788             :     // Copy XMP metadata
    1789        1782 :     if (!psOptions->bNoXMP)
    1790             :     {
    1791        1780 :         char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
    1792        1780 :         if (papszXMP != nullptr && *papszXMP != nullptr)
    1793             :         {
    1794           1 :             poVDS->SetMetadata(papszXMP, "xml:XMP");
    1795             :         }
    1796             :     }
    1797             : 
    1798             :     /* -------------------------------------------------------------------- */
    1799             :     /*      Transfer metadata that remains valid if the spatial             */
    1800             :     /*      arrangement of the data is unaltered.                           */
    1801             :     /* -------------------------------------------------------------------- */
    1802        1782 :     if (bSpatialArrangementPreserved)
    1803             :     {
    1804         538 :         char **papszMD = poSrcDS->GetMetadata("RPC");
    1805         538 :         if (papszMD != nullptr)
    1806           2 :             poVDS->SetMetadata(papszMD, "RPC");
    1807             : 
    1808         538 :         papszMD = poSrcDS->GetMetadata("GEOLOCATION");
    1809         538 :         if (papszMD != nullptr)
    1810           1 :             poVDS->SetMetadata(papszMD, "GEOLOCATION");
    1811             :     }
    1812             :     else
    1813             :     {
    1814        1244 :         char **papszMD = poSrcDSOri->GetMetadata("RPC");
    1815        1244 :         if (papszMD != nullptr)
    1816             :         {
    1817           2 :             papszMD = CSLDuplicate(papszMD);
    1818             : 
    1819             :             double dfSAMP_OFF =
    1820           2 :                 CPLAtof(CSLFetchNameValueDef(papszMD, "SAMP_OFF", "0"));
    1821             :             double dfLINE_OFF =
    1822           2 :                 CPLAtof(CSLFetchNameValueDef(papszMD, "LINE_OFF", "0"));
    1823             :             double dfSAMP_SCALE =
    1824           2 :                 CPLAtof(CSLFetchNameValueDef(papszMD, "SAMP_SCALE", "1"));
    1825             :             double dfLINE_SCALE =
    1826           2 :                 CPLAtof(CSLFetchNameValueDef(papszMD, "LINE_SCALE", "1"));
    1827             : 
    1828           2 :             dfSAMP_OFF -= srcWinOri.dfXOff;
    1829           2 :             dfLINE_OFF -= srcWinOri.dfYOff;
    1830             : 
    1831           2 :             const double df2 = srcWinOri.dfXSize;
    1832           2 :             const double df3 = srcWinOri.dfYSize;
    1833           2 :             const double dfXRatio = nOXSize / df2;
    1834           2 :             const double dfYRatio = nOYSize / df3;
    1835             : 
    1836             :             // For line offset and pixel offset, we need to convert from RPC
    1837             :             // pixel center registration convention to GDAL pixel top-left corner
    1838             :             // registration convention by adding an initial 0.5 shift, and un-apply
    1839             :             // it after scaling.
    1840             : 
    1841           2 :             dfSAMP_OFF += 0.5;
    1842           2 :             dfSAMP_OFF *= dfXRatio;
    1843           2 :             dfSAMP_OFF -= 0.5;
    1844             : 
    1845           2 :             dfLINE_OFF += 0.5;
    1846           2 :             dfLINE_OFF *= dfYRatio;
    1847           2 :             dfLINE_OFF -= 0.5;
    1848             : 
    1849           2 :             dfSAMP_SCALE *= dfXRatio;
    1850           2 :             dfLINE_SCALE *= dfYRatio;
    1851             : 
    1852           4 :             CPLString osField;
    1853           2 :             osField.Printf("%.15g", dfLINE_OFF);
    1854           2 :             papszMD = CSLSetNameValue(papszMD, "LINE_OFF", osField);
    1855             : 
    1856           2 :             osField.Printf("%.15g", dfSAMP_OFF);
    1857           2 :             papszMD = CSLSetNameValue(papszMD, "SAMP_OFF", osField);
    1858             : 
    1859           2 :             osField.Printf("%.15g", dfLINE_SCALE);
    1860           2 :             papszMD = CSLSetNameValue(papszMD, "LINE_SCALE", osField);
    1861             : 
    1862           2 :             osField.Printf("%.15g", dfSAMP_SCALE);
    1863           2 :             papszMD = CSLSetNameValue(papszMD, "SAMP_SCALE", osField);
    1864             : 
    1865           2 :             poVDS->SetMetadata(papszMD, "RPC");
    1866           2 :             CSLDestroy(papszMD);
    1867             :         }
    1868             :     }
    1869             : 
    1870        1782 :     const int nSrcBandCount = psOptions->nBandCount;
    1871             : 
    1872        1782 :     if (psOptions->nRGBExpand != 0)
    1873             :     {
    1874             :         GDALRasterBand *poSrcBand =
    1875          22 :             poSrcDS->GetRasterBand(std::abs(psOptions->anBandList[0]));
    1876          22 :         if (psOptions->anBandList[0] < 0)
    1877           0 :             poSrcBand = poSrcBand->GetMaskBand();
    1878          22 :         GDALColorTable *poColorTable = poSrcBand->GetColorTable();
    1879          22 :         if (poColorTable == nullptr)
    1880             :         {
    1881           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1882             :                      "Error : band %d has no color table",
    1883           0 :                      std::abs(psOptions->anBandList[0]));
    1884           0 :             GDALClose(poVDS);
    1885           0 :             GDALTranslateOptionsFree(psOptions);
    1886           0 :             return nullptr;
    1887             :         }
    1888             : 
    1889             :         /* Check that the color table only contains gray levels */
    1890             :         /* when using -expand gray */
    1891          22 :         if (psOptions->nRGBExpand == 1)
    1892             :         {
    1893           1 :             int nColorCount = poColorTable->GetColorEntryCount();
    1894           3 :             for (int nColor = 0; nColor < nColorCount; nColor++)
    1895             :             {
    1896             :                 const GDALColorEntry *poEntry =
    1897           2 :                     poColorTable->GetColorEntry(nColor);
    1898           2 :                 if (poEntry->c1 != poEntry->c2 || poEntry->c1 != poEntry->c3)
    1899             :                 {
    1900           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1901             :                              "Warning : color table contains non gray levels "
    1902             :                              "colors");
    1903           0 :                     break;
    1904             :                 }
    1905             :             }
    1906             :         }
    1907             : 
    1908          22 :         if (psOptions->nBandCount == 1)
    1909             :         {
    1910          21 :             psOptions->nBandCount = psOptions->nRGBExpand;
    1911             :         }
    1912           1 :         else if (psOptions->nBandCount == 2 &&
    1913           1 :                  (psOptions->nRGBExpand == 3 || psOptions->nRGBExpand == 4))
    1914             :         {
    1915           1 :             psOptions->nBandCount = psOptions->nRGBExpand;
    1916             :         }
    1917             :         else
    1918             :         {
    1919           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1920             :                      "Error : invalid use of -expand option.");
    1921           0 :             GDALClose(poVDS);
    1922           0 :             GDALTranslateOptionsFree(psOptions);
    1923           0 :             return nullptr;
    1924             :         }
    1925             :     }
    1926             : 
    1927             :     // Can be set to TRUE in the band loop too
    1928             :     bool bFilterOutStatsMetadata =
    1929        3499 :         !psOptions->asScaleParams.empty() || psOptions->bUnscale ||
    1930        3499 :         !bSpatialArrangementPreserved || psOptions->nRGBExpand != 0;
    1931             : 
    1932        1782 :     if (static_cast<int>(psOptions->anColorInterp.size()) >
    1933        1782 :         psOptions->nBandCount)
    1934             :     {
    1935           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    1936             :                  "More bands defined in -colorinterp than output bands");
    1937             :     }
    1938             : 
    1939             :     /* ==================================================================== */
    1940             :     /*      Process all bands.                                              */
    1941             :     /* ==================================================================== */
    1942        1782 :     GDALDataType eOutputType = psOptions->eOutputType;
    1943             : 
    1944        6074 :     for (int i = 0; i < psOptions->nBandCount; i++)
    1945             :     {
    1946        4293 :         int nComponent = 0;
    1947        4293 :         int nSrcBand = 0;
    1948             : 
    1949        4293 :         if (psOptions->nRGBExpand != 0)
    1950             :         {
    1951          70 :             if (nSrcBandCount == 2 && psOptions->nRGBExpand == 4 && i == 3)
    1952           1 :                 nSrcBand = psOptions->anBandList[1];
    1953             :             else
    1954             :             {
    1955          69 :                 nSrcBand = psOptions->anBandList[0];
    1956          69 :                 nComponent = i + 1;
    1957             :             }
    1958             :         }
    1959             :         else
    1960             :         {
    1961        4223 :             nSrcBand = psOptions->anBandList[i];
    1962             :         }
    1963             : 
    1964        4293 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(std::abs(nSrcBand));
    1965             : 
    1966             :         /* --------------------------------------------------------------------
    1967             :          */
    1968             :         /*      Select output data type to match source. */
    1969             :         /* --------------------------------------------------------------------
    1970             :          */
    1971             :         GDALRasterBand *poRealSrcBand =
    1972        4293 :             (nSrcBand < 0) ? poSrcBand->GetMaskBand() : poSrcBand;
    1973             :         GDALDataType eBandType;
    1974        4293 :         if (eOutputType == GDT_Unknown)
    1975             :         {
    1976        3755 :             eBandType = poRealSrcBand->GetRasterDataType();
    1977        3755 :             if (eBandType != GDT_Byte && psOptions->nRGBExpand != 0)
    1978             :             {
    1979             :                 // Use case of https://github.com/OSGeo/gdal/issues/9402
    1980           5 :                 if (const auto poColorTable = poRealSrcBand->GetColorTable())
    1981             :                 {
    1982           5 :                     bool bIn0To255Range = true;
    1983           5 :                     const int nColorCount = poColorTable->GetColorEntryCount();
    1984           6 :                     for (int nColor = 0; nColor < nColorCount; nColor++)
    1985             :                     {
    1986             :                         const GDALColorEntry *poEntry =
    1987           5 :                             poColorTable->GetColorEntry(nColor);
    1988           5 :                         if (poEntry->c1 > 255 || poEntry->c2 > 255 ||
    1989           3 :                             poEntry->c3 > 255 || poEntry->c4 > 255)
    1990             :                         {
    1991           4 :                             bIn0To255Range = false;
    1992           4 :                             break;
    1993             :                         }
    1994             :                     }
    1995           5 :                     if (bIn0To255Range)
    1996             :                     {
    1997           1 :                         if (!psOptions->bQuiet)
    1998             :                         {
    1999           1 :                             CPLError(CE_Warning, CPLE_AppDefined,
    2000             :                                      "Using Byte output data type due to range "
    2001             :                                      "of values in color table");
    2002             :                         }
    2003           1 :                         eBandType = GDT_Byte;
    2004             :                     }
    2005             :                 }
    2006           5 :                 eOutputType = eBandType;
    2007             :             }
    2008             :         }
    2009             :         else
    2010             :         {
    2011         538 :             eBandType = eOutputType;
    2012             : 
    2013             :             // Check that we can copy existing statistics
    2014         538 :             GDALDataType eSrcBandType = poRealSrcBand->GetRasterDataType();
    2015             :             const char *pszMin =
    2016         538 :                 poRealSrcBand->GetMetadataItem("STATISTICS_MINIMUM");
    2017             :             const char *pszMax =
    2018         538 :                 poRealSrcBand->GetMetadataItem("STATISTICS_MAXIMUM");
    2019         538 :             if (!bFilterOutStatsMetadata && eBandType != eSrcBandType &&
    2020           4 :                 pszMin != nullptr && pszMax != nullptr)
    2021             :             {
    2022             :                 const bool bSrcIsInteger =
    2023           8 :                     CPL_TO_BOOL(GDALDataTypeIsInteger(eSrcBandType) &&
    2024           4 :                                 !GDALDataTypeIsComplex(eSrcBandType));
    2025             :                 const bool bDstIsInteger =
    2026           7 :                     CPL_TO_BOOL(GDALDataTypeIsInteger(eBandType) &&
    2027           3 :                                 !GDALDataTypeIsComplex(eBandType));
    2028           4 :                 if (bSrcIsInteger && bDstIsInteger)
    2029             :                 {
    2030           3 :                     std::int64_t nDstMin = 0;
    2031           3 :                     std::uint64_t nDstMax = 0;
    2032           3 :                     switch (eBandType)
    2033             :                     {
    2034           1 :                         case GDT_Byte:
    2035           1 :                             nDstMin = std::numeric_limits<std::uint8_t>::min();
    2036           1 :                             nDstMax = std::numeric_limits<std::uint8_t>::max();
    2037           1 :                             break;
    2038           0 :                         case GDT_Int8:
    2039           0 :                             nDstMin = std::numeric_limits<std::int8_t>::min();
    2040           0 :                             nDstMax = std::numeric_limits<std::int8_t>::max();
    2041           0 :                             break;
    2042           2 :                         case GDT_UInt16:
    2043           2 :                             nDstMin = std::numeric_limits<std::uint16_t>::min();
    2044           2 :                             nDstMax = std::numeric_limits<std::uint16_t>::max();
    2045           2 :                             break;
    2046           0 :                         case GDT_Int16:
    2047           0 :                             nDstMin = std::numeric_limits<std::int16_t>::min();
    2048           0 :                             nDstMax = std::numeric_limits<std::int16_t>::max();
    2049           0 :                             break;
    2050           0 :                         case GDT_UInt32:
    2051           0 :                             nDstMin = std::numeric_limits<std::uint32_t>::min();
    2052           0 :                             nDstMax = std::numeric_limits<std::uint32_t>::max();
    2053           0 :                             break;
    2054           0 :                         case GDT_Int32:
    2055           0 :                             nDstMin = std::numeric_limits<std::int32_t>::min();
    2056           0 :                             nDstMax = std::numeric_limits<std::int32_t>::max();
    2057           0 :                             break;
    2058           0 :                         case GDT_UInt64:
    2059           0 :                             nDstMin = std::numeric_limits<std::uint64_t>::min();
    2060           0 :                             nDstMax = std::numeric_limits<std::uint64_t>::max();
    2061           0 :                             break;
    2062           0 :                         case GDT_Int64:
    2063           0 :                             nDstMin = std::numeric_limits<std::int64_t>::min();
    2064           0 :                             nDstMax = std::numeric_limits<std::int64_t>::max();
    2065           0 :                             break;
    2066           0 :                         default:
    2067           0 :                             CPLAssert(false);
    2068             :                             break;
    2069             :                     }
    2070             : 
    2071             :                     try
    2072             :                     {
    2073           3 :                         const auto nMin = std::stoll(pszMin);
    2074           3 :                         const auto nMax = std::stoull(pszMax);
    2075           3 :                         if (nMin < nDstMin || nMax > nDstMax)
    2076           1 :                             bFilterOutStatsMetadata = true;
    2077             :                     }
    2078           0 :                     catch (const std::exception &)
    2079             :                     {
    2080           3 :                     }
    2081             :                 }
    2082             :                 // Float64 is large enough to hold all integer <= 32 bit or
    2083             :                 // float32 values there might be other OK cases, but ere on safe
    2084             :                 // side for now
    2085           1 :                 else if (!((bSrcIsInteger || eSrcBandType == GDT_Float32) &&
    2086             :                            eBandType == GDT_Float64))
    2087             :                 {
    2088           0 :                     bFilterOutStatsMetadata = true;
    2089             :                 }
    2090             :             }
    2091             :         }
    2092             : 
    2093             :         /* --------------------------------------------------------------------
    2094             :          */
    2095             :         /*      Create this band. */
    2096             :         /* --------------------------------------------------------------------
    2097             :          */
    2098        4293 :         CPLStringList aosAddBandOptions;
    2099             :         int nSrcBlockXSize, nSrcBlockYSize;
    2100        4293 :         poSrcBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
    2101        3877 :         if (bKeepResolution &&
    2102        8170 :             (fmod(psOptions->srcWin.dfXOff, nSrcBlockXSize)) == 0 &&
    2103        1411 :             (fmod(psOptions->srcWin.dfYOff, nSrcBlockYSize)) == 0)
    2104             :         {
    2105             :             aosAddBandOptions.SetNameValue("BLOCKXSIZE",
    2106        1190 :                                            CPLSPrintf("%d", nSrcBlockXSize));
    2107             :             aosAddBandOptions.SetNameValue("BLOCKYSIZE",
    2108        1190 :                                            CPLSPrintf("%d", nSrcBlockYSize));
    2109             :         }
    2110             :         const char *pszBlockXSize =
    2111        4293 :             psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE");
    2112        4293 :         if (pszBlockXSize)
    2113          55 :             aosAddBandOptions.SetNameValue("BLOCKXSIZE", pszBlockXSize);
    2114             :         const char *pszBlockYSize =
    2115        4293 :             psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE");
    2116        4293 :         if (pszBlockYSize)
    2117          70 :             aosAddBandOptions.SetNameValue("BLOCKYSIZE", pszBlockYSize);
    2118        4293 :         poVDS->AddBand(eBandType, aosAddBandOptions.List());
    2119             :         VRTSourcedRasterBand *poVRTBand =
    2120        4293 :             static_cast<VRTSourcedRasterBand *>(poVDS->GetRasterBand(i + 1));
    2121             : 
    2122        4293 :         if (nSrcBand < 0)
    2123             :         {
    2124          11 :             poVRTBand->AddMaskBandSource(
    2125             :                 poSrcBand, psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
    2126             :                 psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize,
    2127             :                 dstWin.dfXOff, dstWin.dfYOff, dstWin.dfXSize, dstWin.dfYSize);
    2128             : 
    2129             :             // Color interpretation override
    2130          11 :             if (!psOptions->anColorInterp.empty())
    2131             :             {
    2132          12 :                 if (i < static_cast<int>(psOptions->anColorInterp.size()) &&
    2133           6 :                     psOptions->anColorInterp[i] >= 0)
    2134             :                 {
    2135           6 :                     poVRTBand->SetColorInterpretation(
    2136             :                         static_cast<GDALColorInterp>(
    2137           6 :                             psOptions->anColorInterp[i]));
    2138             :                 }
    2139             :             }
    2140             : 
    2141          11 :             continue;
    2142             :         }
    2143             : 
    2144             :         // Preserve NBITS if no option change values
    2145             :         const char *pszNBits =
    2146        4282 :             poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
    2147          27 :         if (pszNBits && psOptions->nRGBExpand == 0 &&
    2148          26 :             psOptions->asScaleParams.empty() && !psOptions->bUnscale &&
    2149        4309 :             psOptions->eOutputType == GDT_Unknown &&
    2150          13 :             psOptions->osResampling.empty())
    2151             :         {
    2152           1 :             poVRTBand->SetMetadataItem("NBITS", pszNBits, "IMAGE_STRUCTURE");
    2153             :         }
    2154             : 
    2155             :         // Preserve PIXELTYPE if no option change values
    2156        4282 :         if (poSrcBand->GetRasterDataType() == GDT_Byte &&
    2157        4096 :             psOptions->nRGBExpand == 0 && psOptions->asScaleParams.empty() &&
    2158       11897 :             !psOptions->bUnscale && psOptions->eOutputType == GDT_Unknown &&
    2159        3519 :             psOptions->osResampling.empty())
    2160             :         {
    2161        3354 :             poSrcBand->EnablePixelTypeSignedByteWarning(false);
    2162             :             const char *pszPixelType =
    2163        3354 :                 poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    2164        3354 :             poSrcBand->EnablePixelTypeSignedByteWarning(true);
    2165        3354 :             if (pszPixelType)
    2166             :             {
    2167           1 :                 poVRTBand->SetMetadataItem("PIXELTYPE", pszPixelType,
    2168           1 :                                            "IMAGE_STRUCTURE");
    2169             :             }
    2170             :         }
    2171             : 
    2172             :         const char *pszCompression =
    2173        4282 :             poSrcBand->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
    2174        4282 :         if (pszCompression)
    2175             :         {
    2176           9 :             poVRTBand->SetMetadataItem("COMPRESSION", pszCompression,
    2177           9 :                                        "IMAGE_STRUCTURE");
    2178             :         }
    2179             : 
    2180             :         /* --------------------------------------------------------------------
    2181             :          */
    2182             :         /*      Do we need to collect scaling information? */
    2183             :         /* --------------------------------------------------------------------
    2184             :          */
    2185        4282 :         double dfScale = 1.0;
    2186        4282 :         double dfOffset = 0.0;
    2187        4282 :         bool bScale = false;
    2188        4282 :         double dfScaleSrcMin = std::numeric_limits<double>::quiet_NaN();
    2189        4282 :         double dfScaleSrcMax = std::numeric_limits<double>::quiet_NaN();
    2190        4282 :         double dfScaleDstMin = std::numeric_limits<double>::quiet_NaN();
    2191        4282 :         double dfScaleDstMax = std::numeric_limits<double>::quiet_NaN();
    2192        4282 :         bool bExponentScaling = false;
    2193        4282 :         double dfExponent = 0.0;
    2194             : 
    2195        4360 :         if (i < static_cast<int>(psOptions->asScaleParams.size()) &&
    2196          78 :             psOptions->asScaleParams[i].bScale)
    2197             :         {
    2198          67 :             bScale = psOptions->asScaleParams[i].bScale;
    2199          67 :             dfScaleSrcMin = psOptions->asScaleParams[i].dfScaleSrcMin;
    2200          67 :             dfScaleSrcMax = psOptions->asScaleParams[i].dfScaleSrcMax;
    2201          67 :             dfScaleDstMin = psOptions->asScaleParams[i].dfScaleDstMin;
    2202          67 :             dfScaleDstMax = psOptions->asScaleParams[i].dfScaleDstMax;
    2203             :         }
    2204        4248 :         else if (psOptions->asScaleParams.size() == 1 &&
    2205          33 :                  !psOptions->bHasUsedExplicitScaleBand)
    2206             :         {
    2207          32 :             bScale = psOptions->asScaleParams[0].bScale;
    2208          32 :             dfScaleSrcMin = psOptions->asScaleParams[0].dfScaleSrcMin;
    2209          32 :             dfScaleSrcMax = psOptions->asScaleParams[0].dfScaleSrcMax;
    2210          32 :             dfScaleDstMin = psOptions->asScaleParams[0].dfScaleDstMin;
    2211          32 :             dfScaleDstMax = psOptions->asScaleParams[0].dfScaleDstMax;
    2212             :         }
    2213             : 
    2214        4309 :         if (i < static_cast<int>(psOptions->adfExponent.size()) &&
    2215          27 :             psOptions->adfExponent[i] != 0.0)
    2216             :         {
    2217          24 :             bExponentScaling = TRUE;
    2218          24 :             dfExponent = psOptions->adfExponent[i];
    2219             :         }
    2220        4262 :         else if (psOptions->adfExponent.size() == 1 &&
    2221           4 :                  !psOptions->bHasUsedExplicitExponentBand)
    2222             :         {
    2223           3 :             bExponentScaling = TRUE;
    2224           3 :             dfExponent = psOptions->adfExponent[0];
    2225             :         }
    2226             : 
    2227        4282 :         if (bExponentScaling && !bScale)
    2228             :         {
    2229           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2230             :                      "For band %d, -scale should be specified when -exponent "
    2231             :                      "is specified.",
    2232             :                      i + 1);
    2233           1 :             if (pbUsageError)
    2234           0 :                 *pbUsageError = TRUE;
    2235           1 :             GDALTranslateOptionsFree(psOptions);
    2236           1 :             delete poVDS;
    2237           1 :             poSrcDS->Release();
    2238           1 :             return nullptr;
    2239             :         }
    2240             : 
    2241        4281 :         if (bScale && std::isnan(dfScaleSrcMin))
    2242             :         {
    2243          13 :             double adfCMinMax[2] = {};
    2244          13 :             GDALComputeRasterMinMax(poSrcBand, TRUE, adfCMinMax);
    2245          13 :             dfScaleSrcMin = adfCMinMax[0];
    2246          13 :             dfScaleSrcMax = adfCMinMax[1];
    2247             :         }
    2248             : 
    2249        4281 :         if (bScale)
    2250             :         {
    2251             :             /* To avoid a divide by zero */
    2252          99 :             if (dfScaleSrcMax == dfScaleSrcMin)
    2253           0 :                 dfScaleSrcMax += 0.1;
    2254             : 
    2255             :             // Can still occur for very big values
    2256          99 :             if (dfScaleSrcMax == dfScaleSrcMin)
    2257             :             {
    2258           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2259             :                          "-scale cannot be applied due to source "
    2260             :                          "minimum and maximum being equal");
    2261           0 :                 GDALTranslateOptionsFree(psOptions);
    2262           0 :                 delete poVDS;
    2263           0 :                 poSrcDS->Release();
    2264           0 :                 return nullptr;
    2265             :             }
    2266             : 
    2267          99 :             if (std::isnan(dfScaleDstMin))
    2268             :             {
    2269          16 :                 switch (poVRTBand->GetRasterDataType())
    2270             :                 {
    2271           5 :                     case GDT_Byte:
    2272           5 :                         dfScaleDstMin = std::numeric_limits<uint8_t>::lowest();
    2273           5 :                         dfScaleDstMax = std::numeric_limits<uint8_t>::max();
    2274           5 :                         break;
    2275           1 :                     case GDT_Int8:
    2276           1 :                         dfScaleDstMin = std::numeric_limits<int8_t>::lowest();
    2277           1 :                         dfScaleDstMax = std::numeric_limits<int8_t>::max();
    2278           1 :                         break;
    2279           1 :                     case GDT_UInt16:
    2280           1 :                         dfScaleDstMin = std::numeric_limits<uint16_t>::lowest();
    2281           1 :                         dfScaleDstMax = std::numeric_limits<uint16_t>::max();
    2282           1 :                         break;
    2283           1 :                     case GDT_Int16:
    2284             :                     case GDT_CInt16:
    2285           1 :                         dfScaleDstMin = std::numeric_limits<int16_t>::lowest();
    2286           1 :                         dfScaleDstMax = std::numeric_limits<int16_t>::max();
    2287           1 :                         break;
    2288           1 :                     case GDT_UInt32:
    2289           1 :                         dfScaleDstMin = std::numeric_limits<uint32_t>::lowest();
    2290           1 :                         dfScaleDstMax = std::numeric_limits<uint32_t>::max();
    2291           1 :                         break;
    2292           1 :                     case GDT_Int32:
    2293             :                     case GDT_CInt32:
    2294           1 :                         dfScaleDstMin = std::numeric_limits<int32_t>::lowest();
    2295           1 :                         dfScaleDstMax = std::numeric_limits<int32_t>::max();
    2296           1 :                         break;
    2297           1 :                     case GDT_UInt64:
    2298           1 :                         dfScaleDstMin = static_cast<double>(
    2299           1 :                             std::numeric_limits<uint64_t>::lowest());
    2300           1 :                         dfScaleDstMax = static_cast<double>(
    2301           1 :                             std::numeric_limits<uint64_t>::max() - 2048);
    2302           1 :                         break;
    2303           1 :                     case GDT_Int64:
    2304           1 :                         dfScaleDstMin = static_cast<double>(
    2305           1 :                             std::numeric_limits<int64_t>::lowest() + 1024);
    2306           1 :                         dfScaleDstMax = static_cast<double>(
    2307           1 :                             std::numeric_limits<int64_t>::max() - 2048);
    2308           1 :                         break;
    2309           4 :                     case GDT_Float16:
    2310             :                     case GDT_Float32:
    2311             :                     case GDT_Float64:
    2312             :                     case GDT_CFloat16:
    2313             :                     case GDT_CFloat32:
    2314             :                     case GDT_CFloat64:
    2315             :                     case GDT_Unknown:
    2316             :                     case GDT_TypeCount:
    2317           4 :                         dfScaleDstMin = 0;
    2318           4 :                         dfScaleDstMax = 1;
    2319           4 :                         break;
    2320             :                 }
    2321             :             }
    2322             : 
    2323          99 :             if (!bExponentScaling)
    2324             :             {
    2325          73 :                 dfScale = (dfScaleDstMax - dfScaleDstMin) /
    2326          73 :                           (dfScaleSrcMax - dfScaleSrcMin);
    2327          73 :                 dfOffset = -1 * dfScaleSrcMin * dfScale + dfScaleDstMin;
    2328             :             }
    2329             :         }
    2330             : 
    2331        4281 :         if (psOptions->bUnscale)
    2332             :         {
    2333          25 :             dfScale = poSrcBand->GetScale();
    2334          25 :             dfOffset = poSrcBand->GetOffset();
    2335             :         }
    2336             : 
    2337             :         /* --------------------------------------------------------------------
    2338             :          */
    2339             :         /*      Create a simple or complex data source depending on the */
    2340             :         /*      translation type required. */
    2341             :         /* --------------------------------------------------------------------
    2342             :          */
    2343        4281 :         VRTSimpleSource *poSimpleSource = nullptr;
    2344        4281 :         if (psOptions->bUnscale || bScale ||
    2345        4157 :             (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand))
    2346             :         {
    2347         194 :             VRTComplexSource *poSource = new VRTComplexSource();
    2348             : 
    2349             :             /* --------------------------------------------------------------------
    2350             :              */
    2351             :             /*      Set complex parameters. */
    2352             :             /* --------------------------------------------------------------------
    2353             :              */
    2354             : 
    2355         194 :             if (dfOffset != 0.0 || dfScale != 1.0)
    2356             :             {
    2357          89 :                 poSource->SetLinearScaling(dfOffset, dfScale);
    2358             :             }
    2359         105 :             else if (bExponentScaling)
    2360             :             {
    2361          26 :                 poSource->SetPowerScaling(dfExponent, dfScaleSrcMin,
    2362             :                                           dfScaleSrcMax, dfScaleDstMin,
    2363          26 :                                           dfScaleDstMax, !psOptions->bNoClip);
    2364             :             }
    2365             : 
    2366         194 :             poSource->SetColorTableComponent(nComponent);
    2367             : 
    2368             :             int bSuccess;
    2369         194 :             double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
    2370         194 :             if (bSuccess)
    2371             :             {
    2372          16 :                 poSource->SetNoDataValue(dfNoData);
    2373             :             }
    2374             : 
    2375         194 :             poSimpleSource = poSource;
    2376             :         }
    2377             :         else
    2378             :         {
    2379        4087 :             poSimpleSource = new VRTSimpleSource();
    2380             :         }
    2381             : 
    2382        4457 :         poSimpleSource->SetResampling(psOptions->osResampling.empty()
    2383             :                                           ? nullptr
    2384         176 :                                           : psOptions->osResampling.c_str());
    2385        4281 :         poVRTBand->ConfigureSource(
    2386             :             poSimpleSource, poSrcBand, FALSE, psOptions->srcWin.dfXOff,
    2387             :             psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
    2388             :             psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
    2389             :             dstWin.dfXSize, dstWin.dfYSize);
    2390             : 
    2391        4281 :         poVRTBand->AddSource(poSimpleSource);
    2392             : 
    2393             :         /* --------------------------------------------------------------------
    2394             :          */
    2395             :         /*      In case of color table translate, we only set the color */
    2396             :         /*      interpretation other info copied by CopyBandInfo are */
    2397             :         /*      not relevant in RGB expansion. */
    2398             :         /* --------------------------------------------------------------------
    2399             :          */
    2400        4281 :         if (psOptions->nRGBExpand == 1)
    2401             :         {
    2402           1 :             poVRTBand->SetColorInterpretation(GCI_GrayIndex);
    2403             :         }
    2404        4280 :         else if (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand)
    2405             :         {
    2406          69 :             poVRTBand->SetColorInterpretation(
    2407          69 :                 static_cast<GDALColorInterp>(GCI_RedBand + i));
    2408             :         }
    2409             : 
    2410             :         /* --------------------------------------------------------------------
    2411             :          */
    2412             :         /*      copy over some other information of interest. */
    2413             :         /* --------------------------------------------------------------------
    2414             :          */
    2415             :         else
    2416             :         {
    2417        4211 :             CopyBandInfo(poSrcBand, poVRTBand,
    2418        4211 :                          !psOptions->bStats && !bFilterOutStatsMetadata,
    2419        8394 :                          !psOptions->bUnscale && !psOptions->bSetScale &&
    2420        4183 :                              !psOptions->bSetOffset,
    2421        4211 :                          !psOptions->bSetNoData && !psOptions->bUnsetNoData,
    2422        4211 :                          !psOptions->bNoRAT, psOptions);
    2423        8309 :             if (psOptions->asScaleParams.empty() &&
    2424        8309 :                 psOptions->adfExponent.empty() &&
    2425        4098 :                 EQUAL(psOptions->osFormat.c_str(), "GRIB"))
    2426             :             {
    2427           0 :                 char **papszMD_GRIB = poSrcBand->GetMetadata("GRIB");
    2428           0 :                 if (papszMD_GRIB != nullptr)
    2429           0 :                     poVRTBand->SetMetadata(papszMD_GRIB, "GRIB");
    2430             :             }
    2431             :         }
    2432             : 
    2433             :         // Color interpretation override
    2434        4281 :         if (!psOptions->anColorInterp.empty())
    2435             :         {
    2436          80 :             if (i < static_cast<int>(psOptions->anColorInterp.size()) &&
    2437          39 :                 psOptions->anColorInterp[i] >= 0)
    2438             :             {
    2439          19 :                 poVRTBand->SetColorInterpretation(
    2440          19 :                     static_cast<GDALColorInterp>(psOptions->anColorInterp[i]));
    2441             :             }
    2442             :         }
    2443             : 
    2444             :         /* --------------------------------------------------------------------
    2445             :          */
    2446             :         /*      Set a forcible nodata value? */
    2447             :         /* --------------------------------------------------------------------
    2448             :          */
    2449        4281 :         if (psOptions->bSetNoData)
    2450             :         {
    2451             :             const char *pszPixelType =
    2452          80 :                 psOptions->aosCreateOptions.FetchNameValue("PIXELTYPE");
    2453         159 :             if (pszPixelType == nullptr &&
    2454          79 :                 poVRTBand->GetRasterDataType() == GDT_Byte)
    2455             :             {
    2456          29 :                 poVRTBand->EnablePixelTypeSignedByteWarning(false);
    2457             :                 pszPixelType =
    2458          29 :                     poVRTBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    2459          29 :                 poVRTBand->EnablePixelTypeSignedByteWarning(true);
    2460             :             }
    2461             : 
    2462          80 :             bool bCannotBeExactlyRepresented = false;
    2463             : 
    2464          80 :             if (pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE"))
    2465             :             {
    2466           2 :                 char *endptr = nullptr;
    2467             :                 const double dfVal =
    2468           2 :                     CPLStrtod(psOptions->osNoData.c_str(), &endptr);
    2469           2 :                 if (endptr == psOptions->osNoData.c_str() +
    2470           4 :                                   psOptions->osNoData.size() &&
    2471           6 :                     dfVal >= -128.0 && dfVal <= 127.0 &&
    2472           2 :                     static_cast<int8_t>(dfVal) == dfVal)
    2473             :                 {
    2474           2 :                     poVRTBand->SetNoDataValue(dfVal);
    2475             :                 }
    2476             :                 else
    2477             :                 {
    2478           0 :                     bCannotBeExactlyRepresented = true;
    2479           2 :                 }
    2480             :             }
    2481             :             else
    2482             :             {
    2483          78 :                 poVRTBand->SetNoDataValueAsString(psOptions->osNoData.c_str(),
    2484             :                                                   &bCannotBeExactlyRepresented);
    2485             :             }
    2486          80 :             if (bCannotBeExactlyRepresented)
    2487             :             {
    2488           4 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2489             :                          "Nodata value was not set to output band, "
    2490             :                          "as it cannot be represented on its data type.");
    2491             :             }
    2492             :         }
    2493             : 
    2494        4281 :         if (psOptions->bSetScale)
    2495           4 :             poVRTBand->SetScale(psOptions->dfScale);
    2496             : 
    2497        4281 :         if (psOptions->bSetOffset)
    2498           5 :             poVRTBand->SetOffset(psOptions->dfOffset);
    2499             : 
    2500       12748 :         if (psOptions->eMaskMode == MASK_AUTO &&
    2501        4186 :             (poSrcDS->GetRasterBand(1)->GetMaskFlags() & GMF_PER_DATASET) ==
    2502        8467 :                 0 &&
    2503        3816 :             (poSrcBand->GetMaskFlags() & (GMF_ALL_VALID | GMF_NODATA)) == 0)
    2504             :         {
    2505           6 :             if (poVRTBand->CreateMaskBand(poSrcBand->GetMaskFlags()) == CE_None)
    2506             :             {
    2507             :                 VRTSourcedRasterBand *hMaskVRTBand =
    2508           6 :                     cpl::down_cast<VRTSourcedRasterBand *>(
    2509           6 :                         poVRTBand->GetMaskBand());
    2510           6 :                 hMaskVRTBand->AddMaskBandSource(
    2511             :                     poSrcBand, psOptions->srcWin.dfXOff,
    2512             :                     psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
    2513             :                     psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
    2514             :                     dstWin.dfXSize, dstWin.dfYSize);
    2515             :             }
    2516             :         }
    2517             :     }
    2518             : 
    2519        1781 :     if (psOptions->eMaskMode == MASK_USER)
    2520             :     {
    2521             :         GDALRasterBand *poSrcBand =
    2522          23 :             poSrcDS->GetRasterBand(std::abs(psOptions->nMaskBand));
    2523          23 :         if (poSrcBand && poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
    2524             :         {
    2525             :             VRTSourcedRasterBand *hMaskVRTBand =
    2526          23 :                 static_cast<VRTSourcedRasterBand *>(GDALGetMaskBand(
    2527             :                     GDALGetRasterBand(static_cast<GDALDataset *>(poVDS), 1)));
    2528          23 :             if (psOptions->nMaskBand > 0)
    2529          21 :                 hMaskVRTBand->AddSimpleSource(
    2530             :                     poSrcBand, psOptions->srcWin.dfXOff,
    2531             :                     psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
    2532             :                     psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
    2533             :                     dstWin.dfXSize, dstWin.dfYSize);
    2534             :             else
    2535           2 :                 hMaskVRTBand->AddMaskBandSource(
    2536             :                     poSrcBand, psOptions->srcWin.dfXOff,
    2537             :                     psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
    2538             :                     psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
    2539             :                     dstWin.dfXSize, dstWin.dfYSize);
    2540             :         }
    2541             :     }
    2542        3503 :     else if (psOptions->eMaskMode == MASK_AUTO && nSrcBandCount > 0 &&
    2543        1745 :              poSrcDS->GetRasterBand(1)->GetMaskFlags() == GMF_PER_DATASET)
    2544             :     {
    2545           3 :         if (poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
    2546             :         {
    2547             :             VRTSourcedRasterBand *hMaskVRTBand =
    2548           3 :                 static_cast<VRTSourcedRasterBand *>(GDALGetMaskBand(
    2549             :                     GDALGetRasterBand(static_cast<GDALDataset *>(poVDS), 1)));
    2550           3 :             hMaskVRTBand->AddMaskBandSource(
    2551             :                 poSrcDS->GetRasterBand(1), psOptions->srcWin.dfXOff,
    2552             :                 psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
    2553             :                 psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
    2554             :                 dstWin.dfXSize, dstWin.dfYSize);
    2555             :         }
    2556             :     }
    2557             : 
    2558             :     /* -------------------------------------------------------------------- */
    2559             :     /*      Compute stats if required.                                      */
    2560             :     /* -------------------------------------------------------------------- */
    2561        1781 :     if (psOptions->bStats && EQUAL(psOptions->osFormat.c_str(), "COG"))
    2562             :     {
    2563           0 :         psOptions->aosCreateOptions.SetNameValue("STATISTICS", "YES");
    2564             :     }
    2565        1781 :     else if (psOptions->bStats)
    2566             :     {
    2567           2 :         for (int i = 0; i < poVDS->GetRasterCount(); i++)
    2568             :         {
    2569             :             double dfMin, dfMax, dfMean, dfStdDev;
    2570           1 :             poVDS->GetRasterBand(i + 1)->ComputeStatistics(
    2571           1 :                 psOptions->bApproxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
    2572           1 :                 GDALDummyProgress, nullptr);
    2573             :         }
    2574             :     }
    2575             : 
    2576             :     /* -------------------------------------------------------------------- */
    2577             :     /*      Write to the output file using CopyCreate().                    */
    2578             :     /* -------------------------------------------------------------------- */
    2579        2136 :     if (EQUAL(psOptions->osFormat.c_str(), "VRT") &&
    2580         355 :         (psOptions->aosCreateOptions.empty() ||
    2581          16 :          (psOptions->aosCreateOptions.size() == 1 &&
    2582           4 :           psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE")) ||
    2583          16 :          (psOptions->aosCreateOptions.size() == 1 &&
    2584           4 :           psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE")) ||
    2585          12 :          (psOptions->aosCreateOptions.size() == 2 &&
    2586          12 :           psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE") &&
    2587          12 :           psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE"))))
    2588             :     {
    2589         355 :         poVDS->SetDescription(pszDest);
    2590         355 :         hOutDS = GDALDataset::ToHandle(poVDS);
    2591         355 :         if (!EQUAL(pszDest, ""))
    2592             :         {
    2593          28 :             hOutDS = GDALTranslateFlush(hOutDS);
    2594             :         }
    2595             :     }
    2596             :     else
    2597             :     {
    2598        1426 :         hOutDS = GDALCreateCopy(
    2599        1426 :             hDriver, pszDest, GDALDataset::ToHandle(poVDS), psOptions->bStrict,
    2600        1426 :             psOptions->aosCreateOptions.List(), psOptions->pfnProgress,
    2601             :             psOptions->pProgressData);
    2602        1426 :         hOutDS = GDALTranslateFlush(hOutDS);
    2603             : 
    2604        1426 :         GDALClose(poVDS);
    2605             :     }
    2606             : 
    2607        1781 :     poSrcDS->Release();
    2608             : 
    2609        1781 :     GDALTranslateOptionsFree(psOptions);
    2610        1781 :     return hOutDS;
    2611             : }
    2612             : 
    2613             : /************************************************************************/
    2614             : /*                           AttachMetadata()                           */
    2615             : /************************************************************************/
    2616             : 
    2617        1782 : static void AttachMetadata(GDALDatasetH hDS,
    2618             :                            const CPLStringList &aosMetadataOptions)
    2619             : 
    2620             : {
    2621          58 :     for (const auto &[pszKey, pszValue] :
    2622        1840 :          cpl::IterateNameValue(aosMetadataOptions))
    2623             :     {
    2624          29 :         GDALSetMetadataItem(hDS, pszKey, pszValue, nullptr);
    2625             :     }
    2626        1782 : }
    2627             : 
    2628             : /************************************************************************/
    2629             : /*                           AttachDomainMetadata()                     */
    2630             : /************************************************************************/
    2631             : 
    2632        1782 : static void AttachDomainMetadata(GDALDatasetH hDS,
    2633             :                                  const CPLStringList &aosDomainMetadataOptions)
    2634             : 
    2635             : {
    2636        1791 :     for (const char *pszStr : aosDomainMetadataOptions)
    2637             :     {
    2638             : 
    2639           9 :         char *pszKey = nullptr;
    2640           9 :         char *pszDomain = nullptr;
    2641             : 
    2642             :         // parse the DOMAIN:KEY=value, Remainder is KEY=value
    2643             :         const char *pszRemainder =
    2644           9 :             CPLParseNameValueSep(pszStr, &pszDomain, ':');
    2645             : 
    2646           9 :         if (pszDomain && pszRemainder)
    2647             :         {
    2648             : 
    2649             :             const char *pszValue =
    2650           8 :                 CPLParseNameValueSep(pszRemainder, &pszKey, '=');
    2651           8 :             if (pszKey && pszValue)
    2652             :             {
    2653           7 :                 GDALSetMetadataItem(hDS, pszKey, pszValue, pszDomain);
    2654             :             }
    2655             :         }
    2656           9 :         CPLFree(pszKey);
    2657             : 
    2658           9 :         CPLFree(pszDomain);
    2659             :     }
    2660        1782 : }
    2661             : 
    2662             : /************************************************************************/
    2663             : /*                           CopyBandInfo()                            */
    2664             : /************************************************************************/
    2665             : 
    2666             : /* A bit of a clone of VRTRasterBand::CopyCommonInfoFrom(), but we need */
    2667             : /* more and more custom behavior in the context of gdal_translate ... */
    2668             : 
    2669        4211 : static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
    2670             :                          int bCanCopyStatsMetadata, int bCopyScale,
    2671             :                          int bCopyNoData, bool bCopyRAT,
    2672             :                          const GDALTranslateOptions * /*psOptions*/)
    2673             : 
    2674             : {
    2675             : 
    2676        4211 :     if (bCanCopyStatsMetadata)
    2677             :     {
    2678         784 :         poDstBand->SetMetadata(poSrcBand->GetMetadata());
    2679         784 :         if (bCopyRAT)
    2680             :         {
    2681         783 :             poDstBand->SetDefaultRAT(poSrcBand->GetDefaultRAT());
    2682             :         }
    2683             :     }
    2684             :     else
    2685             :     {
    2686        3427 :         char **papszMetadata = poSrcBand->GetMetadata();
    2687        3427 :         char **papszMetadataNew = nullptr;
    2688        3516 :         for (int i = 0; papszMetadata != nullptr && papszMetadata[i] != nullptr;
    2689             :              i++)
    2690             :         {
    2691          89 :             if (!STARTS_WITH(papszMetadata[i], "STATISTICS_"))
    2692             :                 papszMetadataNew =
    2693          71 :                     CSLAddString(papszMetadataNew, papszMetadata[i]);
    2694             :         }
    2695        3427 :         poDstBand->SetMetadata(papszMetadataNew);
    2696        3427 :         CSLDestroy(papszMetadataNew);
    2697             : 
    2698             :         // we need to strip histogram data from the source RAT
    2699        3427 :         if (poSrcBand->GetDefaultRAT() && bCopyRAT)
    2700             :         {
    2701             :             GDALRasterAttributeTable *poNewRAT =
    2702           2 :                 poSrcBand->GetDefaultRAT()->Clone();
    2703             : 
    2704             :             // strip histogram data (as defined by the source RAT)
    2705           2 :             poNewRAT->RemoveStatistics();
    2706           2 :             if (poNewRAT->GetColumnCount())
    2707             :             {
    2708           1 :                 poDstBand->SetDefaultRAT(poNewRAT);
    2709             :             }
    2710             :             // since SetDefaultRAT copies the RAT data we need to delete our
    2711             :             // original
    2712           2 :             delete poNewRAT;
    2713             :         }
    2714             :     }
    2715             : 
    2716        4211 :     poDstBand->SetColorTable(poSrcBand->GetColorTable());
    2717        4211 :     poDstBand->SetColorInterpretation(poSrcBand->GetColorInterpretation());
    2718        4211 :     if (strlen(poSrcBand->GetDescription()) > 0)
    2719           5 :         poDstBand->SetDescription(poSrcBand->GetDescription());
    2720             : 
    2721        4211 :     if (bCopyNoData)
    2722             :     {
    2723        4129 :         int bSuccess = FALSE;
    2724        4129 :         CPL_IGNORE_RET_VAL(poSrcBand->GetNoDataValue(&bSuccess));
    2725        4129 :         if (bSuccess)
    2726             :         {
    2727          95 :             bool bCannotBeExactlyRepresented = false;
    2728          95 :             if (!GDALCopyNoDataValue(poDstBand, poSrcBand,
    2729          95 :                                      &bCannotBeExactlyRepresented) &&
    2730             :                 bCannotBeExactlyRepresented)
    2731             :             {
    2732           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2733             :                          "Source nodata value was not copied to output band, "
    2734             :                          "as it cannot be represented on its data type.");
    2735             :             }
    2736             :         }
    2737             :     }
    2738             : 
    2739        4211 :     if (bCopyScale)
    2740             :     {
    2741        4180 :         poDstBand->SetOffset(poSrcBand->GetOffset());
    2742        4180 :         poDstBand->SetScale(poSrcBand->GetScale());
    2743             :     }
    2744             : 
    2745        4211 :     poDstBand->SetCategoryNames(poSrcBand->GetCategoryNames());
    2746             : 
    2747             :     // Copy unit only if the range of pixel values is not modified
    2748        4989 :     if (bCanCopyStatsMetadata && bCopyScale &&
    2749         778 :         !EQUAL(poSrcBand->GetUnitType(), ""))
    2750          26 :         poDstBand->SetUnitType(poSrcBand->GetUnitType());
    2751        4211 : }
    2752             : 
    2753             : /************************************************************************/
    2754             : /*                             GetColorInterp()                         */
    2755             : /************************************************************************/
    2756             : 
    2757          26 : static int GetColorInterp(const char *pszStr)
    2758             : {
    2759          26 :     if (EQUAL(pszStr, "undefined"))
    2760           5 :         return GCI_Undefined;
    2761          21 :     const int eInterp = GDALGetColorInterpretationByName(pszStr);
    2762          21 :     if (eInterp != GCI_Undefined)
    2763          20 :         return eInterp;
    2764           1 :     CPLError(CE_Warning, CPLE_NotSupported,
    2765             :              "Unsupported color interpretation: %s", pszStr);
    2766           1 :     return -1;
    2767             : }
    2768             : 
    2769             : /************************************************************************/
    2770             : /*                     GDALTranslateOptionsGetParser()                  */
    2771             : /************************************************************************/
    2772             : 
    2773             : static std::unique_ptr<GDALArgumentParser>
    2774        2704 : GDALTranslateOptionsGetParser(GDALTranslateOptions *psOptions,
    2775             :                               GDALTranslateOptionsForBinary *psOptionsForBinary)
    2776             : {
    2777             :     auto argParser = std::make_unique<GDALArgumentParser>(
    2778        2704 :         "gdal_translate", /* bForBinary=*/psOptionsForBinary != nullptr);
    2779             : 
    2780        2704 :     argParser->add_description(
    2781             :         _("Convert raster data between different formats, with potential "
    2782        2704 :           "subsetting, resampling, and rescaling pixels in the process."));
    2783             : 
    2784        2704 :     argParser->add_epilog(_("For more details, consult "
    2785        2704 :                             "https://gdal.org/programs/gdal_translate.html"));
    2786             : 
    2787        2704 :     argParser->add_output_type_argument(psOptions->eOutputType);
    2788             : 
    2789        2704 :     argParser->add_argument("-if")
    2790        2704 :         .append()
    2791        5408 :         .metavar("<format>")
    2792             :         .action(
    2793           6 :             [psOptionsForBinary](const std::string &s)
    2794             :             {
    2795           3 :                 if (psOptionsForBinary)
    2796             :                 {
    2797           3 :                     if (GDALGetDriverByName(s.c_str()) == nullptr)
    2798             :                     {
    2799           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2800             :                                  "%s is not a recognized driver", s.c_str());
    2801             :                     }
    2802             :                     psOptionsForBinary->aosAllowedInputDrivers.AddString(
    2803           3 :                         s.c_str());
    2804             :                 }
    2805        2704 :             })
    2806        2704 :         .help(_("Format/driver name(s) to try when opening the input file."));
    2807             : 
    2808        2704 :     argParser->add_output_format_argument(psOptions->osFormat);
    2809             : 
    2810        2704 :     argParser->add_quiet_argument(&(psOptions->bQuiet));
    2811             : 
    2812        2704 :     argParser->add_argument("-b")
    2813        2704 :         .append()
    2814        5408 :         .metavar("<band>")
    2815             :         .action(
    2816        1208 :             [psOptions](const std::string &s)
    2817             :             {
    2818         593 :                 const char *pszBand = s.c_str();
    2819         593 :                 bool bMask = false;
    2820         593 :                 if (EQUAL(pszBand, "mask"))
    2821          11 :                     pszBand = "mask,1";
    2822         593 :                 if (STARTS_WITH_CI(pszBand, "mask,"))
    2823             :                 {
    2824          11 :                     bMask = true;
    2825          11 :                     pszBand += 5;
    2826             :                     /* If we use the source mask band as a regular band */
    2827             :                     /* don't create a target mask band by default */
    2828          11 :                     if (!psOptions->bParsedMaskArgument)
    2829          11 :                         psOptions->eMaskMode = MASK_DISABLED;
    2830             :                 }
    2831         593 :                 const int nBand = atoi(pszBand);
    2832         593 :                 if (nBand < 1)
    2833             :                 {
    2834             :                     throw std::invalid_argument(CPLSPrintf(
    2835           0 :                         "Unrecognizable band number (%s).", s.c_str()));
    2836             :                 }
    2837             : 
    2838         593 :                 psOptions->nBandCount++;
    2839         593 :                 psOptions->anBandList.emplace_back(nBand * (bMask ? -1 : 1));
    2840        3297 :             })
    2841        2704 :         .help(_("Select input band(s)"));
    2842             : 
    2843        2704 :     argParser->add_argument("-mask")
    2844        5408 :         .metavar("<mask>")
    2845             :         .action(
    2846          50 :             [psOptions](const std::string &s)
    2847             :             {
    2848          24 :                 psOptions->bParsedMaskArgument = true;
    2849          24 :                 const char *pszBand = s.c_str();
    2850          24 :                 if (EQUAL(pszBand, "none"))
    2851             :                 {
    2852           1 :                     psOptions->eMaskMode = MASK_DISABLED;
    2853             :                 }
    2854          23 :                 else if (EQUAL(pszBand, "auto"))
    2855             :                 {
    2856           0 :                     psOptions->eMaskMode = MASK_AUTO;
    2857             :                 }
    2858             :                 else
    2859             :                 {
    2860          23 :                     bool bMask = false;
    2861             : 
    2862          23 :                     if (EQUAL(pszBand, "mask"))
    2863           1 :                         pszBand = "mask,1";
    2864          23 :                     if (STARTS_WITH_CI(pszBand, "mask,"))
    2865             :                     {
    2866           2 :                         bMask = true;
    2867           2 :                         pszBand += 5;
    2868             :                     }
    2869          23 :                     const int nBand = atoi(pszBand);
    2870          23 :                     if (nBand < 1)
    2871             :                     {
    2872             :                         throw std::invalid_argument(CPLSPrintf(
    2873           0 :                             "Unrecognizable band number (%s).", s.c_str()));
    2874             :                     }
    2875             : 
    2876          23 :                     psOptions->eMaskMode = MASK_USER;
    2877          23 :                     psOptions->nMaskBand = nBand;
    2878          23 :                     if (bMask)
    2879           2 :                         psOptions->nMaskBand *= -1;
    2880             :                 }
    2881        2728 :             })
    2882        2704 :         .help(_("Select an input band to create output dataset mask band"));
    2883             : 
    2884        2704 :     argParser->add_argument("-expand")
    2885        5408 :         .metavar("gray|rgb|rgba")
    2886             :         .action(
    2887          44 :             [psOptions](const std::string &s)
    2888             :             {
    2889          22 :                 if (EQUAL(s.c_str(), "gray"))
    2890           1 :                     psOptions->nRGBExpand = 1;
    2891          21 :                 else if (EQUAL(s.c_str(), "rgb"))
    2892          15 :                     psOptions->nRGBExpand = 3;
    2893           6 :                 else if (EQUAL(s.c_str(), "rgba"))
    2894           6 :                     psOptions->nRGBExpand = 4;
    2895             :                 else
    2896             :                 {
    2897             :                     throw std::invalid_argument(CPLSPrintf(
    2898             :                         "Value %s unsupported. Only gray, rgb or rgba are "
    2899             :                         "supported.",
    2900           0 :                         s.c_str()));
    2901             :                 }
    2902        2726 :             })
    2903             :         .help(_("To expose a dataset with 1 band with a color table as a "
    2904        2704 :                 "dataset with 3 (RGB) or 4 (RGBA) bands."));
    2905             : 
    2906             :     {
    2907        2704 :         auto &group = argParser->add_mutually_exclusive_group();
    2908        2704 :         group.add_argument("-strict")
    2909        2704 :             .store_into(psOptions->bStrict)
    2910        2704 :             .help(_("Enable strict mode"));
    2911             : 
    2912        2704 :         group.add_argument("-not_strict")
    2913        2704 :             .flag()
    2914           0 :             .action([psOptions](const std::string &)
    2915        2704 :                     { psOptions->bStrict = false; })
    2916        2704 :             .help(_("Disable strict mode"));
    2917             :     }
    2918             : 
    2919        2704 :     argParser->add_argument("-outsize")
    2920        5408 :         .metavar("<xsize[%]|0> <ysize[%]|0>")
    2921        2704 :         .nargs(2)
    2922        2704 :         .help(_("Set the size of the output file."));
    2923             : 
    2924        2704 :     argParser->add_argument("-tr")
    2925        5408 :         .metavar("<xres> <yes>")
    2926        2704 :         .nargs(2)
    2927        2704 :         .scan<'g', double>()
    2928        2704 :         .help(_("Set target resolution."));
    2929             : 
    2930        2704 :     argParser->add_argument("-ovr")
    2931        5408 :         .metavar("<level>|AUTO|AUTO-<n>|NONE")
    2932             :         .action(
    2933          31 :             [psOptions](const std::string &s)
    2934             :             {
    2935          16 :                 const char *pszOvLevel = s.c_str();
    2936          16 :                 if (EQUAL(pszOvLevel, "AUTO"))
    2937           0 :                     psOptions->nOvLevel = OVR_LEVEL_AUTO;
    2938          16 :                 else if (STARTS_WITH_CI(pszOvLevel, "AUTO-"))
    2939           4 :                     psOptions->nOvLevel =
    2940           4 :                         OVR_LEVEL_AUTO - atoi(pszOvLevel + strlen("AUTO-"));
    2941          12 :                 else if (EQUAL(pszOvLevel, "NONE"))
    2942           1 :                     psOptions->nOvLevel = OVR_LEVEL_NONE;
    2943          11 :                 else if (CPLGetValueType(pszOvLevel) == CPL_VALUE_INTEGER)
    2944          10 :                     psOptions->nOvLevel = atoi(pszOvLevel);
    2945             :                 else
    2946             :                 {
    2947             :                     throw std::invalid_argument(CPLSPrintf(
    2948           1 :                         "Invalid value '%s' for -ovr option", pszOvLevel));
    2949             :                 }
    2950        2719 :             })
    2951        2704 :         .help(_("Specify which overview level of source file must be used"));
    2952             : 
    2953        2704 :     if (psOptionsForBinary)
    2954             :     {
    2955         142 :         argParser->add_argument("-sds")
    2956         142 :             .store_into(psOptionsForBinary->bCopySubDatasets)
    2957         142 :             .help(_("Copy subdatasets"));
    2958             :     }
    2959             : 
    2960        2704 :     argParser->add_argument("-r")
    2961        5408 :         .metavar("nearest,bilinear,cubic,cubicspline,lanczos,average,mode")
    2962        2704 :         .store_into(psOptions->osResampling)
    2963        2704 :         .help(_("Resampling algorithm."));
    2964             : 
    2965             :     {
    2966        2704 :         auto &group = argParser->add_mutually_exclusive_group();
    2967        2704 :         group.add_argument("-scale")
    2968        5408 :             .metavar("[<src_min> <src_max> [<dst_min> <dst_max>]]")
    2969             :             //.nargs(0, 4)
    2970        2704 :             .append()
    2971        2704 :             .scan<'g', double>()
    2972             :             .help(_("Rescale the input pixels values from the range src_min to "
    2973        2704 :                     "src_max to the range dst_min to dst_max."));
    2974             : 
    2975        2704 :         group.add_argument("-scale_X")
    2976        5408 :             .metavar("[<src_min> <src_max> [<dst_min> <dst_max>]]")
    2977             :             //.nargs(0, 4)
    2978        2704 :             .append()
    2979        2704 :             .scan<'g', double>()
    2980        2704 :             .help(_("Rescale the input pixels values for band X."));
    2981             : 
    2982        2704 :         group.add_argument("-unscale")
    2983        2704 :             .store_into(psOptions->bUnscale)
    2984             :             .help(_("Apply the scale/offset metadata for the bands to convert "
    2985        2704 :                     "scaled values to unscaled values."));
    2986             :     }
    2987             : 
    2988             :     {
    2989        2704 :         auto &group = argParser->add_mutually_exclusive_group();
    2990        2704 :         group.add_argument("-exponent")
    2991        5408 :             .metavar("<value>")
    2992        2704 :             .scan<'g', double>()
    2993             :             .help(_(
    2994        2704 :                 "Exponent to apply non-linear scaling with a power function"));
    2995             : 
    2996        2704 :         group.add_argument("-exponent_X")
    2997        2704 :             .append()
    2998        5408 :             .metavar("<value>")
    2999        2704 :             .scan<'g', double>()
    3000             :             .help(
    3001             :                 _("Exponent to apply non-linear scaling with a power function, "
    3002        2704 :                   "for band X"));
    3003             :     }
    3004             : 
    3005        2704 :     argParser->add_argument("-srcwin")
    3006        5408 :         .metavar("<xoff> <yoff> <xsize> <ysize>")
    3007        2704 :         .nargs(4)
    3008        2704 :         .scan<'g', double>()
    3009             :         .help(_("Selects a subwindow from the source image based on pixel/line "
    3010        2704 :                 "location."));
    3011             : 
    3012        2704 :     argParser->add_argument("-projwin")
    3013        5408 :         .metavar("<ulx> <uly> <lrx> <lry>")
    3014        2704 :         .nargs(4)
    3015        2704 :         .scan<'g', double>()
    3016             :         .help(_("Selects a subwindow from the source image based on "
    3017        2704 :                 "georeferenced coordinates."));
    3018             : 
    3019        2704 :     argParser->add_argument("-projwin_srs")
    3020        5408 :         .metavar("<srs_def>")
    3021        2704 :         .store_into(psOptions->osProjSRS)
    3022             :         .help(_("Specifies the SRS in which to interpret the coordinates given "
    3023        2704 :                 "with -projwin."));
    3024             : 
    3025        2704 :     argParser->add_argument("-epo")
    3026        2704 :         .flag()
    3027             :         .action(
    3028          12 :             [psOptions](const std::string &)
    3029             :             {
    3030          12 :                 psOptions->bErrorOnPartiallyOutside = true;
    3031          12 :                 psOptions->bErrorOnCompletelyOutside = true;
    3032        2704 :             })
    3033        2704 :         .help(_("Error when Partially Outside."));
    3034             : 
    3035        2704 :     argParser->add_argument("-eco")
    3036        2704 :         .store_into(psOptions->bErrorOnCompletelyOutside)
    3037        2704 :         .help(_("Error when Completely Outside."));
    3038             : 
    3039        2704 :     argParser->add_argument("-a_srs")
    3040        5408 :         .metavar("<srs_def>")
    3041        2704 :         .store_into(psOptions->osOutputSRS)
    3042        2704 :         .help(_("Override the projection for the output file."));
    3043             : 
    3044        2704 :     argParser->add_argument("-a_coord_epoch")
    3045        5408 :         .metavar("<epoch>")
    3046        2704 :         .store_into(psOptions->dfOutputCoordinateEpoch)
    3047        2704 :         .help(_("Assign a coordinate epoch."));
    3048             : 
    3049        2704 :     argParser->add_argument("-a_ullr")
    3050        5408 :         .metavar("<ulx> <uly> <lrx> <lry>")
    3051        2704 :         .nargs(4)
    3052        2704 :         .scan<'g', double>()
    3053             :         .help(
    3054        2704 :             _("Assign/override the georeferenced bounds of the output file."));
    3055             : 
    3056        2704 :     argParser->add_argument("-a_nodata")
    3057        5408 :         .metavar("<value>|none")
    3058        2704 :         .help(_("Assign a specified nodata value to output bands."));
    3059             : 
    3060        2704 :     argParser->add_argument("-a_gt")
    3061        5408 :         .metavar("<gt(0)> <gt(1)> <gt(2)> <gt(3)> <gt(4)> <gt(5)>")
    3062        2704 :         .nargs(6)
    3063        2704 :         .scan<'g', double>()
    3064        2704 :         .help(_("Assign/override the geotransform of the output file."));
    3065             : 
    3066        2704 :     argParser->add_argument("-a_scale")
    3067        5408 :         .metavar("<value>")
    3068        2704 :         .store_into(psOptions->dfScale)
    3069        2704 :         .help(_("Set band scaling value."));
    3070             : 
    3071        2704 :     argParser->add_argument("-a_offset")
    3072        5408 :         .metavar("<value>")
    3073        2704 :         .store_into(psOptions->dfOffset)
    3074        2704 :         .help(_("Set band offset value."));
    3075             : 
    3076        2704 :     argParser->add_argument("-nogcp")
    3077        2704 :         .store_into(psOptions->bNoGCP)
    3078             :         .help(_("Do not copy the GCPs in the source dataset to the output "
    3079        2704 :                 "dataset."));
    3080             : 
    3081        2704 :     argParser->add_argument("-gcp")
    3082        5408 :         .metavar("<pixel> <line> <easting> <northing> [<elevation>]")
    3083        2704 :         .nargs(4, 5)
    3084        2704 :         .append()
    3085        2704 :         .scan<'g', double>()
    3086             :         .help(
    3087        2704 :             _("Add the indicated ground control point to the output dataset."));
    3088             : 
    3089        2704 :     argParser->add_argument("-colorinterp")
    3090             :         .metavar("{red|green|blue|alpha|gray|undefined|pan|coastal|rededge|nir|"
    3091        5408 :                  "swir|mwir|lwir|...},...")
    3092             :         .action(
    3093          26 :             [psOptions](const std::string &s)
    3094             :             {
    3095          10 :                 CPLStringList aosList(CSLTokenizeString2(s.c_str(), ",", 0));
    3096           5 :                 psOptions->anColorInterp.resize(aosList.size());
    3097          21 :                 for (int j = 0; j < aosList.size(); j++)
    3098             :                 {
    3099          16 :                     psOptions->anColorInterp[j] = GetColorInterp(aosList[j]);
    3100             :                 }
    3101        2709 :             })
    3102        2704 :         .help(_("Override the color interpretation of all specified bands."));
    3103             : 
    3104        2704 :     argParser->add_argument("-colorinterp_X")
    3105        2704 :         .append()
    3106             :         .metavar("{red|green|blue|alpha|gray|undefined|pan|coastal|rededge|nir|"
    3107        5408 :                  "swir|mwir|lwir|...}")
    3108        2704 :         .help(_("Override the color interpretation of band X."));
    3109             : 
    3110             :     {
    3111        2704 :         auto &group = argParser->add_mutually_exclusive_group();
    3112        2704 :         group.add_argument("-stats")
    3113        2704 :             .flag()
    3114             :             .action(
    3115           4 :                 [psOptions](const std::string &)
    3116             :                 {
    3117           4 :                     psOptions->bStats = true;
    3118           4 :                     psOptions->bApproxStats = false;
    3119        2704 :                 })
    3120        2704 :             .help(_("Force (re)computation of statistics."));
    3121             : 
    3122        2704 :         group.add_argument("-approx_stats")
    3123        2704 :             .flag()
    3124             :             .action(
    3125           0 :                 [psOptions](const std::string &)
    3126             :                 {
    3127           0 :                     psOptions->bStats = true;
    3128           0 :                     psOptions->bApproxStats = true;
    3129        2704 :                 })
    3130        2704 :             .help(_("Force (re)computation of approximate statistics."));
    3131             :     }
    3132             : 
    3133        2704 :     argParser->add_argument("-norat")
    3134        2704 :         .store_into(psOptions->bNoRAT)
    3135        2704 :         .help(_("Do not copy source RAT into destination dataset."));
    3136             : 
    3137        2704 :     argParser->add_argument("-noxmp")
    3138        2704 :         .store_into(psOptions->bNoXMP)
    3139        2704 :         .help(_("Do not copy the XMP metadata into destination dataset."));
    3140             : 
    3141        2704 :     argParser->add_creation_options_argument(psOptions->aosCreateOptions);
    3142             : 
    3143             :     argParser->add_metadata_item_options_argument(
    3144        2704 :         psOptions->aosMetadataOptions);
    3145             : 
    3146        2704 :     argParser->add_argument("-dmo")
    3147        5408 :         .metavar("<DOMAIN>:<KEY>=<VALUE>")
    3148        2704 :         .append()
    3149           9 :         .action([psOptions](const std::string &s)
    3150        2713 :                 { psOptions->aosDomainMetadataOptions.AddString(s.c_str()); })
    3151             :         .help(_("Passes a metadata key and value in specified domain to set on "
    3152        2704 :                 "the output dataset if possible."));
    3153             : 
    3154             :     argParser->add_open_options_argument(
    3155        2704 :         psOptionsForBinary ? &(psOptionsForBinary->aosOpenOptions) : nullptr);
    3156             : 
    3157             :     // Undocumented option used by gdal_translate_fuzzer
    3158        2704 :     argParser->add_argument("-limit_outsize")
    3159        2704 :         .hidden()
    3160        2704 :         .store_into(psOptions->nLimitOutSize);
    3161             : 
    3162             :     // Undocumented option used by gdal raster convert
    3163        2704 :     argParser->add_argument("--no-overwrite")
    3164        2704 :         .store_into(psOptions->bNoOverwrite)
    3165        2704 :         .hidden();
    3166             : 
    3167             :     // Undocumented option used by gdal raster scale
    3168        2704 :     argParser->add_argument("--no-clip")
    3169        2704 :         .store_into(psOptions->bNoClip)
    3170        2704 :         .hidden();
    3171             : 
    3172        2704 :     if (psOptionsForBinary)
    3173             :     {
    3174         142 :         argParser->add_argument("input_file")
    3175         284 :             .metavar("<input_file>")
    3176         142 :             .store_into(psOptionsForBinary->osSource)
    3177         142 :             .help(_("Input file."));
    3178             : 
    3179         142 :         argParser->add_argument("output_file")
    3180         284 :             .metavar("<output_file>")
    3181         142 :             .store_into(psOptionsForBinary->osDest)
    3182         142 :             .help(_("Output file."));
    3183             :     }
    3184             : 
    3185        2704 :     return argParser;
    3186             : }
    3187             : 
    3188             : /************************************************************************/
    3189             : /*                      GDALTranslateGetParserUsage()                   */
    3190             : /************************************************************************/
    3191             : 
    3192           4 : std::string GDALTranslateGetParserUsage()
    3193             : {
    3194             :     try
    3195             :     {
    3196           8 :         GDALTranslateOptions sOptions;
    3197           8 :         auto argParser = GDALTranslateOptionsGetParser(&sOptions, nullptr);
    3198           4 :         return argParser->usage();
    3199             :     }
    3200           0 :     catch (const std::exception &err)
    3201             :     {
    3202           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
    3203           0 :                  err.what());
    3204           0 :         return std::string();
    3205             :     }
    3206             : }
    3207             : 
    3208             : /************************************************************************/
    3209             : /*                             GDALTranslateOptionsNew()                */
    3210             : /************************************************************************/
    3211             : 
    3212             : /**
    3213             :  * Allocates a GDALTranslateOptions struct.
    3214             :  *
    3215             :  * @param papszArgv NULL terminated list of options (potentially including
    3216             :  * filename and open options too), or NULL. The accepted options are the ones of
    3217             :  * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
    3218             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    3219             :  * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
    3220             :  *                           GDALTranslateOptionsForBinaryNew() prior to this
    3221             :  * function. Will be filled with potentially present filename, open options,...
    3222             :  * @return pointer to the allocated GDALTranslateOptions struct. Must be freed
    3223             :  * with GDALTranslateOptionsFree().
    3224             :  *
    3225             :  * @since GDAL 2.1
    3226             :  */
    3227             : 
    3228             : GDALTranslateOptions *
    3229        2702 : GDALTranslateOptionsNew(char **papszArgv,
    3230             :                         GDALTranslateOptionsForBinary *psOptionsForBinary)
    3231             : {
    3232        5404 :     auto psOptions = std::make_unique<GDALTranslateOptions>();
    3233             : 
    3234             :     /* -------------------------------------------------------------------- */
    3235             :     /*      Pre-processing for custom syntax that ArgumentParser does not   */
    3236             :     /*      support.                                                        */
    3237             :     /* -------------------------------------------------------------------- */
    3238             : 
    3239        5404 :     CPLStringList aosArgv;
    3240        2702 :     const int argc = CSLCount(papszArgv);
    3241       16317 :     for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
    3242             :          i++)
    3243             :     {
    3244       13617 :         if (i + 4 < argc && EQUAL(papszArgv[i], "-gcp"))
    3245             :         {
    3246             :             /* -gcp pixel line easting northing [elev] */
    3247          29 :             psOptions->nGCPCount++;
    3248          29 :             psOptions->pasGCPs = static_cast<GDAL_GCP *>(CPLRealloc(
    3249          29 :                 psOptions->pasGCPs, sizeof(GDAL_GCP) * psOptions->nGCPCount));
    3250          29 :             GDALInitGCPs(1, psOptions->pasGCPs + psOptions->nGCPCount - 1);
    3251             : 
    3252          29 :             psOptions->pasGCPs[psOptions->nGCPCount - 1].dfGCPPixel =
    3253          29 :                 CPLAtofM(papszArgv[++i]);
    3254          29 :             psOptions->pasGCPs[psOptions->nGCPCount - 1].dfGCPLine =
    3255          29 :                 CPLAtofM(papszArgv[++i]);
    3256          29 :             psOptions->pasGCPs[psOptions->nGCPCount - 1].dfGCPX =
    3257          29 :                 CPLAtofM(papszArgv[++i]);
    3258          29 :             psOptions->pasGCPs[psOptions->nGCPCount - 1].dfGCPY =
    3259          29 :                 CPLAtofM(papszArgv[++i]);
    3260             : 
    3261          29 :             char *endptr = nullptr;
    3262          56 :             if (papszArgv[i + 1] != nullptr &&
    3263          27 :                 (CPLStrtod(papszArgv[i + 1], &endptr) != 0.0 ||
    3264          27 :                  papszArgv[i + 1][0] == '0'))
    3265             :             {
    3266             :                 /* Check that last argument is really a number and not a
    3267             :                  * filename */
    3268             :                 /* looking like a number (see ticket #863) */
    3269          13 :                 if (endptr && *endptr == 0)
    3270          13 :                     psOptions->pasGCPs[psOptions->nGCPCount - 1].dfGCPZ =
    3271          13 :                         CPLAtofM(papszArgv[++i]);
    3272          29 :             }
    3273             : 
    3274             :             /* should set id and info? */
    3275             :         }
    3276             : 
    3277       13588 :         else if (EQUAL(papszArgv[i], "-scale") ||
    3278       13532 :                  STARTS_WITH_CI(papszArgv[i], "-scale_"))
    3279             :         {
    3280          69 :             int nIndex = 0;
    3281          69 :             if (STARTS_WITH_CI(papszArgv[i], "-scale_"))
    3282             :             {
    3283          26 :                 if (!psOptions->bHasUsedExplicitScaleBand &&
    3284          13 :                     !psOptions->asScaleParams.empty())
    3285             :                 {
    3286           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3287             :                              "Cannot mix -scale and -scale_XX syntax");
    3288           0 :                     return nullptr;
    3289             :                 }
    3290          13 :                 psOptions->bHasUsedExplicitScaleBand = true;
    3291          13 :                 nIndex = atoi(papszArgv[i] + 7);
    3292          13 :                 if (nIndex <= 0 || nIndex > 65535)
    3293             :                 {
    3294           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3295           0 :                              "Invalid parameter name: %s", papszArgv[i]);
    3296           0 :                     return nullptr;
    3297             :                 }
    3298          13 :                 nIndex--;
    3299             :             }
    3300             :             else
    3301             :             {
    3302          56 :                 if (psOptions->bHasUsedExplicitScaleBand)
    3303             :                 {
    3304           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3305             :                              "Cannot mix -scale and -scale_XX syntax");
    3306           0 :                     return nullptr;
    3307             :                 }
    3308          56 :                 nIndex = static_cast<int>(psOptions->asScaleParams.size());
    3309             :             }
    3310             : 
    3311          69 :             if (nIndex >= static_cast<int>(psOptions->asScaleParams.size()))
    3312             :             {
    3313          69 :                 psOptions->asScaleParams.resize(nIndex + 1);
    3314             :             }
    3315          69 :             psOptions->asScaleParams[nIndex].bScale = true;
    3316          69 :             bool bScanForDst = false;
    3317          69 :             if (i < argc - 2 && EQUAL(papszArgv[i + 1], "NaN") &&
    3318           1 :                 EQUAL(papszArgv[i + 2], "NaN"))
    3319             :             {
    3320           1 :                 bScanForDst = true;
    3321           1 :                 i += 2;
    3322             :             }
    3323          68 :             else if (i < argc - 2 && ArgIsNumeric(papszArgv[i + 1]))
    3324             :             {
    3325          56 :                 if (!ArgIsNumeric(papszArgv[i + 2]))
    3326             :                 {
    3327           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    3328             :                              "Value of -scale must be numeric");
    3329           0 :                     return nullptr;
    3330             :                 }
    3331          56 :                 psOptions->asScaleParams[nIndex].dfScaleSrcMin =
    3332          56 :                     CPLAtofM(papszArgv[i + 1]);
    3333          56 :                 psOptions->asScaleParams[nIndex].dfScaleSrcMax =
    3334          56 :                     CPLAtofM(papszArgv[i + 2]);
    3335          56 :                 bScanForDst = true;
    3336          56 :                 i += 2;
    3337             :             }
    3338          69 :             if (i < argc - 2 && bScanForDst && ArgIsNumeric(papszArgv[i + 1]))
    3339             :             {
    3340          53 :                 if (!ArgIsNumeric(papszArgv[i + 2]))
    3341             :                 {
    3342           1 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    3343             :                              "Value of -scale must be numeric");
    3344           1 :                     return nullptr;
    3345             :                 }
    3346          52 :                 psOptions->asScaleParams[nIndex].dfScaleDstMin =
    3347          52 :                     CPLAtofM(papszArgv[i + 1]);
    3348          52 :                 psOptions->asScaleParams[nIndex].dfScaleDstMax =
    3349          52 :                     CPLAtofM(papszArgv[i + 2]);
    3350          52 :                 i += 2;
    3351          68 :             }
    3352             :         }
    3353             : 
    3354       13519 :         else if ((EQUAL(papszArgv[i], "-exponent") ||
    3355       13499 :                   STARTS_WITH_CI(papszArgv[i], "-exponent_")) &&
    3356          24 :                  papszArgv[i + 1])
    3357             :         {
    3358          24 :             int nIndex = 0;
    3359          24 :             if (STARTS_WITH_CI(papszArgv[i], "-exponent_"))
    3360             :             {
    3361           8 :                 if (!psOptions->bHasUsedExplicitExponentBand &&
    3362           4 :                     !psOptions->adfExponent.empty())
    3363             :                 {
    3364           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3365             :                              "Cannot mix -exponent and -exponent_XX syntax");
    3366           0 :                     return nullptr;
    3367             :                 }
    3368           4 :                 psOptions->bHasUsedExplicitExponentBand = true;
    3369           4 :                 nIndex = atoi(papszArgv[i] + 10);
    3370           4 :                 if (nIndex <= 0 || nIndex > 65535)
    3371             :                 {
    3372           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3373           0 :                              "Invalid parameter name: %s", papszArgv[i]);
    3374           0 :                     return nullptr;
    3375             :                 }
    3376           4 :                 nIndex--;
    3377             :             }
    3378             :             else
    3379             :             {
    3380          20 :                 if (psOptions->bHasUsedExplicitExponentBand)
    3381             :                 {
    3382           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3383             :                              "Cannot mix -exponent and -exponent_XX syntax");
    3384           0 :                     return nullptr;
    3385             :                 }
    3386          20 :                 nIndex = static_cast<int>(psOptions->adfExponent.size());
    3387             :             }
    3388             : 
    3389          24 :             if (nIndex >= static_cast<int>(psOptions->adfExponent.size()))
    3390             :             {
    3391          24 :                 psOptions->adfExponent.resize(nIndex + 1);
    3392             :             }
    3393          24 :             double dfExponent = CPLAtofM(papszArgv[++i]);
    3394          24 :             psOptions->adfExponent[nIndex] = dfExponent;
    3395             :         }
    3396             : 
    3397       13495 :         else if (STARTS_WITH_CI(papszArgv[i], "-colorinterp_") &&
    3398          11 :                  papszArgv[i + 1])
    3399             :         {
    3400          11 :             int nIndex = atoi(papszArgv[i] + strlen("-colorinterp_"));
    3401          11 :             if (nIndex <= 0 || nIndex > 65535)
    3402             :             {
    3403           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    3404           1 :                          "Invalid parameter name: %s", papszArgv[i]);
    3405           1 :                 return nullptr;
    3406             :             }
    3407          10 :             nIndex--;
    3408             : 
    3409          10 :             if (nIndex >= static_cast<int>(psOptions->anColorInterp.size()))
    3410             :             {
    3411          10 :                 psOptions->anColorInterp.resize(nIndex + 1, -1);
    3412             :             }
    3413          10 :             ++i;
    3414          10 :             psOptions->anColorInterp[nIndex] = GetColorInterp(papszArgv[i]);
    3415             :         }
    3416             : 
    3417             :         // argparser will be confused if the value of a string argument
    3418             :         // starts with a negative sign.
    3419       13484 :         else if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
    3420             :         {
    3421          72 :             ++i;
    3422         144 :             const std::string s = papszArgv[i];
    3423          72 :             if (EQUAL(s.c_str(), "none") || EQUAL(s.c_str(), "null"))
    3424             :             {
    3425           2 :                 psOptions->bUnsetNoData = true;
    3426             :             }
    3427             :             else
    3428             :             {
    3429          70 :                 psOptions->bSetNoData = true;
    3430          70 :                 psOptions->osNoData = s;
    3431          72 :             }
    3432             :         }
    3433             : 
    3434             :         else
    3435             :         {
    3436       13412 :             aosArgv.AddString(papszArgv[i]);
    3437             :         }
    3438             :     }
    3439             : 
    3440             :     try
    3441             :     {
    3442             : 
    3443             :         auto argParser =
    3444        5400 :             GDALTranslateOptionsGetParser(psOptions.get(), psOptionsForBinary);
    3445             : 
    3446        2700 :         argParser->parse_args_without_binary_name(aosArgv.List());
    3447             : 
    3448        2696 :         psOptions->bSetScale = argParser->is_used("-a_scale");
    3449        2696 :         psOptions->bSetOffset = argParser->is_used("-a_offset");
    3450             : 
    3451        2716 :         if (auto adfULLR = argParser->present<std::vector<double>>("-a_ullr"))
    3452             :         {
    3453          20 :             CPLAssert(psOptions->adfULLR.size() == adfULLR->size());
    3454         100 :             for (size_t i = 0; i < adfULLR->size(); ++i)
    3455          80 :                 psOptions->adfULLR[i] = (*adfULLR)[i];
    3456             :         }
    3457             : 
    3458        2699 :         if (auto adfGT = argParser->present<std::vector<double>>("-a_gt"))
    3459             :         {
    3460           3 :             CPLAssert(psOptions->adfGT.size() == adfGT->size());
    3461          21 :             for (size_t i = 0; i < adfGT->size(); ++i)
    3462          18 :                 psOptions->adfGT[i] = (*adfGT)[i];
    3463             :         }
    3464             : 
    3465        2696 :         bool bOutsizeExplicitlySet = false;
    3466        2696 :         if (auto aosOutSize =
    3467        5392 :                 argParser->present<std::vector<std::string>>("-outsize"))
    3468             :         {
    3469         224 :             if ((*aosOutSize)[0].back() == '%')
    3470          30 :                 psOptions->dfOXSizePct = CPLAtofM((*aosOutSize)[0].c_str());
    3471             :             else
    3472         194 :                 psOptions->nOXSizePixel = atoi((*aosOutSize)[0].c_str());
    3473             : 
    3474         224 :             if ((*aosOutSize)[1].back() == '%')
    3475          29 :                 psOptions->dfOYSizePct = CPLAtofM((*aosOutSize)[1].c_str());
    3476             :             else
    3477         195 :                 psOptions->nOYSizePixel = atoi((*aosOutSize)[1].c_str());
    3478         224 :             bOutsizeExplicitlySet = true;
    3479             :         }
    3480             : 
    3481        2696 :         if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
    3482             :         {
    3483          12 :             psOptions->dfXRes = (*adfTargetRes)[0];
    3484          12 :             psOptions->dfYRes = fabs((*adfTargetRes)[1]);
    3485          12 :             if (psOptions->dfXRes == 0 || psOptions->dfYRes == 0)
    3486             :             {
    3487           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    3488             :                          "Wrong value for -tr parameters.");
    3489           0 :                 return nullptr;
    3490             :             }
    3491             :         }
    3492             : 
    3493        3605 :         if (auto adfSrcWin = argParser->present<std::vector<double>>("-srcwin"))
    3494             :         {
    3495         909 :             psOptions->srcWin.dfXOff = (*adfSrcWin)[0];
    3496         909 :             psOptions->srcWin.dfYOff = (*adfSrcWin)[1];
    3497         909 :             psOptions->srcWin.dfXSize = (*adfSrcWin)[2];
    3498         909 :             psOptions->srcWin.dfYSize = (*adfSrcWin)[3];
    3499             :         }
    3500             : 
    3501        2696 :         if (auto adfProjWin =
    3502        5392 :                 argParser->present<std::vector<double>>("-projwin"))
    3503             :         {
    3504         189 :             psOptions->dfULX = (*adfProjWin)[0];
    3505         189 :             psOptions->dfULY = (*adfProjWin)[1];
    3506         189 :             psOptions->dfLRX = (*adfProjWin)[2];
    3507         189 :             psOptions->dfLRY = (*adfProjWin)[3];
    3508             :         }
    3509             : 
    3510        2696 :         if (psOptions->nGCPCount > 0 && psOptions->bNoGCP)
    3511             :         {
    3512           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    3513             :                      "-nogcp and -gcp cannot be used as the same time");
    3514           0 :             return nullptr;
    3515             :         }
    3516             : 
    3517         224 :         if (bOutsizeExplicitlySet && psOptions->nOXSizePixel == 0 &&
    3518        2921 :             psOptions->dfOXSizePct == 0.0 && psOptions->nOYSizePixel == 0 &&
    3519           1 :             psOptions->dfOYSizePct == 0.0)
    3520             :         {
    3521           1 :             CPLError(CE_Failure, CPLE_NotSupported, "-outsize %d %d invalid.",
    3522           1 :                      psOptions->nOXSizePixel, psOptions->nOYSizePixel);
    3523           1 :             return nullptr;
    3524             :         }
    3525             : 
    3526        2695 :         if (!psOptions->asScaleParams.empty() && psOptions->bUnscale)
    3527             :         {
    3528           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
    3529             :                      "-scale and -unscale cannot be used as the same time");
    3530           1 :             return nullptr;
    3531             :         }
    3532             : 
    3533        2694 :         if (psOptionsForBinary)
    3534             :         {
    3535         139 :             psOptionsForBinary->bQuiet = psOptions->bQuiet;
    3536         139 :             psOptionsForBinary->aosCreateOptions = psOptions->aosCreateOptions;
    3537         139 :             if (!psOptions->osFormat.empty())
    3538          82 :                 psOptionsForBinary->osFormat = psOptions->osFormat;
    3539             :         }
    3540             : 
    3541        2694 :         return psOptions.release();
    3542             :     }
    3543           4 :     catch (const std::exception &err)
    3544             :     {
    3545           4 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
    3546           4 :         return nullptr;
    3547             :     }
    3548             : }
    3549             : 
    3550             : /************************************************************************/
    3551             : /*                        GDALTranslateOptionsFree()                    */
    3552             : /************************************************************************/
    3553             : 
    3554             : /**
    3555             :  * Frees the GDALTranslateOptions struct.
    3556             :  *
    3557             :  * @param psOptions the options struct for GDALTranslate().
    3558             :  *
    3559             :  * @since GDAL 2.1
    3560             :  */
    3561             : 
    3562        5367 : void GDALTranslateOptionsFree(GDALTranslateOptions *psOptions)
    3563             : {
    3564        5367 :     delete psOptions;
    3565        5367 : }
    3566             : 
    3567             : /************************************************************************/
    3568             : /*                 GDALTranslateOptionsSetProgress()                    */
    3569             : /************************************************************************/
    3570             : 
    3571             : /**
    3572             :  * Set a progress function.
    3573             :  *
    3574             :  * @param psOptions the options struct for GDALTranslate().
    3575             :  * @param pfnProgress the progress callback.
    3576             :  * @param pProgressData the user data for the progress callback.
    3577             :  *
    3578             :  * @since GDAL 2.1
    3579             :  */
    3580             : 
    3581         386 : void GDALTranslateOptionsSetProgress(GDALTranslateOptions *psOptions,
    3582             :                                      GDALProgressFunc pfnProgress,
    3583             :                                      void *pProgressData)
    3584             : {
    3585         386 :     psOptions->pfnProgress = pfnProgress;
    3586         386 :     psOptions->pProgressData = pProgressData;
    3587         386 :     if (pfnProgress == GDALTermProgress)
    3588         128 :         psOptions->bQuiet = false;
    3589         386 : }

Generated by: LCOV version 1.14