LCOV - code coverage report
Current view: top level - apps - gdal_translate_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1421 1612 88.2 %
Date: 2025-07-01 22:47:05 Functions: 27 29 93.1 %

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

Generated by: LCOV version 1.14