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

Generated by: LCOV version 1.14