LCOV - code coverage report
Current view: top level - apps - gdal_translate_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1378 1634 84.3 %
Date: 2024-05-03 15:49:35 Functions: 31 33 93.9 %

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

Generated by: LCOV version 1.14