LCOV - code coverage report
Current view: top level - apps - gdal_translate_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1499 1692 88.6 %
Date: 2026-08-18 10:50:40 Functions: 28 30 93.3 %

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

Generated by: LCOV version 1.14