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

Generated by: LCOV version 1.14