LCOV - code coverage report
Current view: top level - apps - gdal_translate_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1431 1695 84.4 %
Date: 2025-03-28 11:40:40 Functions: 30 32 93.8 %

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

Generated by: LCOV version 1.14