• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.15
/apps/gdal_translate_lib.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Utilities
4
 * Purpose:  GDAL Image Translator Program
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, 2002, Frank Warmerdam
9
 * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2015, Faza Mahamood
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14

15
#include "cpl_port.h"
16
#include "gdal_utils.h"
17
#include "gdal_utils_priv.h"
18
#include "gdalargumentparser.h"
19

20
#include <cmath>
21
#include <cstdlib>
22
#include <cstring>
23

24
#include <algorithm>
25
#include <array>
26
#include <limits>
27
#include <set>
28

29
#include "commonutils.h"
30
#include "cpl_conv.h"
31
#include "cpl_error.h"
32
#include "cpl_json.h"
33
#include "cpl_progress.h"
34
#include "cpl_string.h"
35
#include "cpl_vsi.h"
36
#include "gdal.h"
37
#include "gdal_priv.h"
38
#include "gdal_priv_templates.hpp"
39
#include "gdal_rat.h"
40
#include "gdal_vrt.h"
41
#include "ogr_core.h"
42
#include "ogr_spatialref.h"
43
#include "vrtdataset.h"
44

45
static void AttachMetadata(GDALDatasetH, const CPLStringList &);
46
static void AttachDomainMetadata(GDALDatasetH, const CPLStringList &);
47

48
static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
49
                         int bCanCopyStatsMetadata, int bCopyScale,
50
                         int bCopyNoData, bool bCopyRAT,
51
                         const GDALTranslateOptions *psOptions);
52

53
typedef enum
54
{
55
    MASK_DISABLED,
56
    MASK_AUTO,
57
    MASK_USER
58
} MaskMode;
59

60
/************************************************************************/
61
/*                         GDALTranslateScaleParams                     */
62
/************************************************************************/
63

64
/** scaling parameters for use in GDALTranslateOptions.
65
 */
66
struct GDALTranslateScaleParams
67
{
68
    /*! scaling is done only if it is set to TRUE. This is helpful when there is
69
       a need to scale only certain bands. */
70
    bool bScale = false;
71

72
    /*! the range of input pixel values which need to be scaled.
73
        If not set, the input range is automatically computed from the source data. */
74
    double dfScaleSrcMin = std::numeric_limits<double>::quiet_NaN();
75
    double dfScaleSrcMax = std::numeric_limits<double>::quiet_NaN();
76

77
    /*! the range of output pixel values. */
78
    double dfScaleDstMin = std::numeric_limits<double>::quiet_NaN();
79
    double dfScaleDstMax = std::numeric_limits<double>::quiet_NaN();
80
};
81

82
/************************************************************************/
83
/*                         GDALTranslateOptions                         */
84
/************************************************************************/
85

86
/** Options for use with GDALTranslate(). GDALTranslateOptions* must be
87
 * allocated and freed with GDALTranslateOptionsNew() and
88
 * GDALTranslateOptionsFree() respectively.
89
 */
90
struct GDALTranslateOptions
91
{
92

93
    /*! output format. Use the short format name. */
94
    std::string osFormat{};
95

96
    /*! allow or suppress progress monitor and other non-error output */
97
    bool bQuiet = false;
98

99
    /*! the progress function to use */
100
    GDALProgressFunc pfnProgress = GDALDummyProgress;
101

102
    /*! pointer to the progress data variable */
103
    void *pProgressData = nullptr;
104

105
    /*! for the output bands to be of the indicated data type */
106
    GDALDataType eOutputType = GDT_Unknown;
107

108
    /*! Used only by parser logic */
109
    bool bParsedMaskArgument = false;
110

111
    MaskMode eMaskMode = MASK_AUTO;
112

113
    /*! number of input bands to write to the output file, or to reorder bands
114
     */
115
    int nBandCount = 0;
116

117
    /*! list of input bands to write to the output file, or to reorder bands.
118
       The value 1 corresponds to the 1st band. */
119
    std::vector<int> anBandList{}; /* negative value of panBandList[i] means
120
                                      mask band of ABS(panBandList[i]) */
121

122
    /*! size of the output file. GDALTranslateOptions::nOXSizePixel is in pixels
123
       and GDALTranslateOptions::nOYSizePixel is in lines. If one of the two
124
       values is set to 0, its value will be determined from the other one,
125
       while maintaining the aspect ratio of the source dataset */
126
    int nOXSizePixel = 0;
127
    int nOYSizePixel = 0;
128

129
    /*! size of the output file. GDALTranslateOptions::dfOXSizePct and
130
       GDALTranslateOptions::dfOYSizePct are fraction of the input image size.
131
       The value 100 means 100%. If one of the two values is set to 0, its value
132
       will be determined from the other one, while maintaining the aspect ratio
133
       of the source dataset */
134
    double dfOXSizePct = 0;
135
    double dfOYSizePct = 0;
136

137
    /*! list of creation options to the output format driver */
138
    CPLStringList aosCreateOptions{};
139

140
    /*! subwindow from the source image for copying based on pixel/line location
141
     */
142
    struct PixelLineWindow
143
    {
144
        double dfXOff{0};
145
        double dfYOff{0};
146
        double dfXSize{0};
147
        double dfYSize{0};
148
    };
149

150
    PixelLineWindow srcWin{};
151

152
    /*! don't be forgiving of mismatches and lost data when translating to the
153
     * output format */
154
    bool bStrict = false;
155

156
    /*! apply the scale/offset metadata for the bands to convert scaled values
157
     * to unscaled values. It is also often necessary to reset the output
158
     * datatype with GDALTranslateOptions::eOutputType */
159
    bool bUnscale = false;
160

161
    bool bSetScale = false;
162

163
    double dfScale = 1;
164

165
    bool bSetOffset = false;
166

167
    double dfOffset = 0;
168

169
    /*! the list of scale parameters for each band. */
170
    std::vector<GDALTranslateScaleParams> asScaleParams{};
171

172
    /*! It is set to TRUE, when scale parameters are specific to each band */
173
    bool bHasUsedExplicitScaleBand = false;
174

175
    bool bNoClip = false;
176

177
    /*! to apply non-linear scaling with a power function. It is the list of
178
       exponents of the power function (must be positive). This option must be
179
       used with GDALTranslateOptions::asScaleParams. If
180
        GDALTranslateOptions::adfExponent.size() is 1, it is applied to all
181
       bands of the output image. */
182
    std::vector<double> adfExponent{};
183

184
    bool bHasUsedExplicitExponentBand = false;
185

186
    /*! list of metadata key and value to set on the output dataset if possible. */
187
    CPLStringList aosMetadataOptions{};
188

189
    /*! list of metadata key and value in a domain to set on the output dataset if possible. */
190
    CPLStringList aosDomainMetadataOptions{};
191

192
    /*! override the projection for the output file. The SRS may be any of the
193
       usual GDAL/OGR forms, complete WKT, PROJ.4, EPSG:n or a file containing
194
       the WKT. */
195
    std::string osOutputSRS{};
196

197
    /*! Coordinate epoch of output SRS */
198
    double dfOutputCoordinateEpoch = 0;
199

200
    /*! does not copy source GCP into destination dataset (when TRUE) */
201
    bool bNoGCP = false;
202

203
    /*! list of GCPs to be added to the output dataset */
204
    std::vector<gdal::GCP> asGCPs{};
205

206
    /*! assign/override the georeferenced bounds of the output file. This
207
       assigns georeferenced bounds to the output file, ignoring what would have
208
       been derived from the source file. So this does not cause reprojection to
209
       the specified SRS. */
210
    std::array<double, 4> adfULLR{{0, 0, 0, 0}};
211

212
    /*! assign/override the geotransform of the output file. This
213
       assigns a geotransform to the output file, ignoring what would have
214
       been derived from the source file. So this does not cause reprojection to
215
       the specified SRS. */
216
    GDALGeoTransform gt{0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
217

218
    /*! set a nodata value specified in GDALTranslateOptions::osNoData to the
219
     * output bands */
220
    bool bSetNoData = 0;
221

222
    /*! avoid setting a nodata value to the output file if one exists for the
223
     * source file */
224
    bool bUnsetNoData = 0;
225

226
    /*! Assign a specified nodata value to output bands (
227
       GDALTranslateOptions::bSetNoData option should be set). Note that if the
228
       input dataset has a nodata value, this does not cause pixel values that
229
       are equal to that nodata value to be changed to the value specified. */
230
    std::string osNoData{};
231

232
    /*! to expose a dataset with 1 band with a color table as a dataset with
233
        3 (RGB) or 4 (RGBA) bands. Useful for output drivers such as JPEG,
234
        JPEG2000, MrSID, ECW that don't support color indexed datasets.
235
        The 1 value enables to expand a dataset with a color table that only
236
        contains gray levels to a gray indexed dataset. */
237
    int nRGBExpand = 0;
238

239
    int nMaskBand = 0; /* negative value means mask band of ABS(nMaskBand) */
240

241
    /*! force recomputation of statistics */
242
    bool bStats = false;
243

244
    bool bApproxStats = false;
245

246
    /*! If this option is set, GDALTranslateOptions::adfSrcWin or
247
       (GDALTranslateOptions::dfULX, GDALTranslateOptions::dfULY,
248
       GDALTranslateOptions::dfLRX, GDALTranslateOptions::dfLRY) values that
249
       falls partially outside the source raster extent will be considered as an
250
       error. The default behavior is to accept such requests. */
251
    bool bErrorOnPartiallyOutside = false;
252

253
    /*! Same as bErrorOnPartiallyOutside, except that the criterion for
254
        erroring out is when the request falls completely outside the
255
        source raster extent. */
256
    bool bErrorOnCompletelyOutside = false;
257

258
    /*! does not copy source RAT into destination dataset (when TRUE) */
259
    bool bNoRAT = false;
260

261
    /*! resampling algorithm
262
        nearest (default), bilinear, cubic, cubicspline, lanczos, average, mode
263
     */
264
    std::string osResampling{};
265

266
    /*! target resolution. The values must be expressed in georeferenced units.
267
        Both must be positive values. This is exclusive with
268
       GDALTranslateOptions::nOXSizePixel (or
269
       GDALTranslateOptions::dfOXSizePct), GDALTranslateOptions::nOYSizePixel
270
        (or GDALTranslateOptions::dfOYSizePct), GDALTranslateOptions::adfULLR,
271
        and GDALTranslateOptions::gt.
272
     */
273
    double dfXRes = 0;
274
    double dfYRes = 0;
275

276
    /*! subwindow from the source image for copying (like
277
       GDALTranslateOptions::adfSrcWin) but with the corners given in
278
       georeferenced coordinates (by default expressed in the SRS of the
279
       dataset. Can be changed with osProjSRS) */
280
    double dfULX = 0;
281
    double dfULY = 0;
282
    double dfLRX = 0;
283
    double dfLRY = 0;
284

285
    /*! SRS in which to interpret the coordinates given with
286
       GDALTranslateOptions::dfULX, GDALTranslateOptions::dfULY,
287
       GDALTranslateOptions::dfLRX, GDALTranslateOptions::dfLRY. The SRS may be
288
       any of the usual GDAL/OGR forms, complete WKT, PROJ.4, EPSG:n or a file
289
       containing the WKT. Note that this does not cause reprojection of the
290
        dataset to the specified SRS. */
291
    std::string osProjSRS{};
292

293
    int nLimitOutSize = 0;
294

295
    // Array of color interpretations per band. Should be a GDALColorInterp
296
    // value, or -1 if no override.
297
    std::vector<int> anColorInterp{};
298

299
    /*! does not copy source XMP into destination dataset (when TRUE) */
300
    bool bNoXMP = false;
301

302
    /*! overview level of source file to be used */
303
    int nOvLevel = OVR_LEVEL_AUTO;
304

305
    /*! set to true to prevent overwriting existing dataset */
306
    bool bNoOverwrite = false;
307

308
    GDALTranslateOptions() = default;
2,731✔
309
    GDALTranslateOptions(const GDALTranslateOptions &) = default;
2,703✔
310
    GDALTranslateOptions &operator=(const GDALTranslateOptions &) = delete;
311
};
312

313
/************************************************************************/
314
/*                              SrcToDst()                              */
315
/************************************************************************/
316

317
static void SrcToDst(double dfX, double dfY, double dfSrcXOff, double dfSrcYOff,
52✔
318
                     double dfSrcXSize, double dfSrcYSize, double dfDstXOff,
319
                     double dfDstYOff, double dfDstXSize, double dfDstYSize,
320
                     double &dfXOut, double &dfYOut)
321

322
{
323
    dfXOut = ((dfX - dfSrcXOff) / dfSrcXSize) * dfDstXSize + dfDstXOff;
52✔
324
    dfYOut = ((dfY - dfSrcYOff) / dfSrcYSize) * dfDstYSize + dfDstYOff;
52✔
325
}
52✔
326

327
/************************************************************************/
328
/*                          GetSrcDstWindow()                           */
329
/************************************************************************/
330

331
static bool FixSrcDstWindow(GDALTranslateOptions::PixelLineWindow &srcWin,
1,789✔
332
                            GDALTranslateOptions::PixelLineWindow &dstWin,
333
                            int nSrcRasterXSize, int nSrcRasterYSize)
334

335
{
336
    const double dfSrcXOff = srcWin.dfXOff;
1,789✔
337
    const double dfSrcYOff = srcWin.dfYOff;
1,789✔
338
    const double dfSrcXSize = srcWin.dfXSize;
1,789✔
339
    const double dfSrcYSize = srcWin.dfYSize;
1,789✔
340

341
    const double dfDstXOff = dstWin.dfXOff;
1,789✔
342
    const double dfDstYOff = dstWin.dfYOff;
1,789✔
343
    const double dfDstXSize = dstWin.dfXSize;
1,789✔
344
    const double dfDstYSize = dstWin.dfYSize;
1,789✔
345

346
    bool bModifiedX = false;
1,789✔
347
    bool bModifiedY = false;
1,789✔
348

349
    double dfModifiedSrcXOff = dfSrcXOff;
1,789✔
350
    double dfModifiedSrcYOff = dfSrcYOff;
1,789✔
351

352
    double dfModifiedSrcXSize = dfSrcXSize;
1,789✔
353
    double dfModifiedSrcYSize = dfSrcYSize;
1,789✔
354

355
    /* -------------------------------------------------------------------- */
356
    /*      Clamp within the bounds of the available source data.           */
357
    /* -------------------------------------------------------------------- */
358
    if (dfModifiedSrcXOff < 0)
1,789✔
359
    {
360
        dfModifiedSrcXSize += dfModifiedSrcXOff;
9✔
361
        dfModifiedSrcXOff = 0;
9✔
362

363
        bModifiedX = true;
9✔
364
    }
365

366
    if (dfModifiedSrcYOff < 0)
1,789✔
367
    {
368
        dfModifiedSrcYSize += dfModifiedSrcYOff;
4✔
369
        dfModifiedSrcYOff = 0;
4✔
370
        bModifiedY = true;
4✔
371
    }
372

373
    if (dfModifiedSrcXOff + dfModifiedSrcXSize > nSrcRasterXSize)
1,789✔
374
    {
375
        dfModifiedSrcXSize = nSrcRasterXSize - dfModifiedSrcXOff;
20✔
376
        bModifiedX = true;
20✔
377
    }
378

379
    if (dfModifiedSrcYOff + dfModifiedSrcYSize > nSrcRasterYSize)
1,789✔
380
    {
381
        dfModifiedSrcYSize = nSrcRasterYSize - dfModifiedSrcYOff;
24✔
382
        bModifiedY = true;
24✔
383
    }
384

385
    /* -------------------------------------------------------------------- */
386
    /*      Don't do anything if the requesting region is completely off    */
387
    /*      the source image.                                               */
388
    /* -------------------------------------------------------------------- */
389
    if (dfModifiedSrcXOff >= nSrcRasterXSize ||
1,789✔
390
        dfModifiedSrcYOff >= nSrcRasterYSize || dfModifiedSrcXSize <= 0 ||
1,789✔
391
        dfModifiedSrcYSize <= 0)
392
    {
393
        return false;
2✔
394
    }
395

396
    srcWin.dfXOff = dfModifiedSrcXOff;
1,787✔
397
    srcWin.dfYOff = dfModifiedSrcYOff;
1,787✔
398
    srcWin.dfXSize = dfModifiedSrcXSize;
1,787✔
399
    srcWin.dfYSize = dfModifiedSrcYSize;
1,787✔
400

401
    /* -------------------------------------------------------------------- */
402
    /*      If we haven't had to modify the source rectangle, then the      */
403
    /*      destination rectangle must be the whole region.                 */
404
    /* -------------------------------------------------------------------- */
405
    if (!bModifiedX && !bModifiedY)
1,787✔
406
        return true;
1,761✔
407

408
    /* -------------------------------------------------------------------- */
409
    /*      Now transform this possibly reduced request back into the       */
410
    /*      destination buffer coordinates in case the output region is     */
411
    /*      less than the whole buffer.                                     */
412
    /* -------------------------------------------------------------------- */
413
    double dfDstULX, dfDstULY, dfDstLRX, dfDstLRY;
414

415
    SrcToDst(dfModifiedSrcXOff, dfModifiedSrcYOff, dfSrcXOff, dfSrcYOff,
26✔
416
             dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
417
             dfDstYSize, dfDstULX, dfDstULY);
418
    SrcToDst(dfModifiedSrcXOff + dfModifiedSrcXSize,
26✔
419
             dfModifiedSrcYOff + dfModifiedSrcYSize, dfSrcXOff, dfSrcYOff,
420
             dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
421
             dfDstYSize, dfDstLRX, dfDstLRY);
422

423
    double dfModifiedDstXOff = dfDstXOff;
26✔
424
    double dfModifiedDstYOff = dfDstYOff;
26✔
425
    double dfModifiedDstXSize = dfDstXSize;
26✔
426
    double dfModifiedDstYSize = dfDstYSize;
26✔
427

428
    if (bModifiedX)
26✔
429
    {
430
        dfModifiedDstXOff = dfDstULX - dfDstXOff;
24✔
431
        dfModifiedDstXSize = (dfDstLRX - dfDstXOff) - dfModifiedDstXOff;
24✔
432

433
        dfModifiedDstXOff = std::max(0.0, dfModifiedDstXOff);
24✔
434
        if (dfModifiedDstXOff + dfModifiedDstXSize > dfDstXSize)
24✔
435
            dfModifiedDstXSize = dfDstXSize - dfModifiedDstXOff;
×
436
    }
437

438
    if (bModifiedY)
26✔
439
    {
440
        dfModifiedDstYOff = dfDstULY - dfDstYOff;
23✔
441
        dfModifiedDstYSize = (dfDstLRY - dfDstYOff) - dfModifiedDstYOff;
23✔
442

443
        dfModifiedDstYOff = std::max(0.0, dfModifiedDstYOff);
23✔
444
        if (dfModifiedDstYOff + dfModifiedDstYSize > dfDstYSize)
23✔
445
            dfModifiedDstYSize = dfDstYSize - dfModifiedDstYOff;
×
446
    }
447

448
    if (dfModifiedDstXSize <= 0.0 || dfModifiedDstYSize <= 0.0)
26✔
449
    {
450
        return false;
×
451
    }
452

453
    dstWin.dfXOff = dfModifiedDstXOff;
26✔
454
    dstWin.dfYOff = dfModifiedDstYOff;
26✔
455
    dstWin.dfXSize = dfModifiedDstXSize;
26✔
456
    dstWin.dfYSize = dfModifiedDstYSize;
26✔
457

458
    return true;
26✔
459
}
460

461
/************************************************************************/
462
/*                        GDALTranslateFlush()                          */
463
/************************************************************************/
464

465
static GDALDatasetH GDALTranslateFlush(GDALDatasetH hOutDS)
2,366✔
466
{
467
    if (hOutDS != nullptr)
2,366✔
468
    {
469
        CPLErr eErrBefore = CPLGetLastErrorType();
2,267✔
470
        GDALFlushCache(hOutDS);
2,267✔
471
        if (eErrBefore == CE_None && CPLGetLastErrorType() != CE_None)
2,267✔
472
        {
473
            GDALClose(hOutDS);
1✔
474
            hOutDS = nullptr;
1✔
475
        }
476
    }
477
    return hOutDS;
2,366✔
478
}
479

480
/************************************************************************/
481
/*                    EditISIS3MetadataForBandChange()                  */
482
/************************************************************************/
483

484
static CPLJSONObject Clone(const CPLJSONObject &obj)
1✔
485
{
486
    auto serialized = obj.Format(CPLJSONObject::PrettyFormat::Plain);
2✔
487
    CPLJSONDocument oJSONDocument;
2✔
488
    const GByte *pabyData = reinterpret_cast<const GByte *>(serialized.c_str());
1✔
489
    oJSONDocument.LoadMemory(pabyData);
1✔
490
    return oJSONDocument.GetRoot();
2✔
491
}
492

493
static void ReworkArray(CPLJSONObject &container, const CPLJSONObject &obj,
4✔
494
                        int nSrcBandCount,
495
                        const GDALTranslateOptions *psOptions)
496
{
497
    auto oArray = obj.ToArray();
8✔
498
    if (oArray.Size() == nSrcBandCount)
4✔
499
    {
500
        CPLJSONArray oNewArray;
8✔
501
        for (int nBand : psOptions->anBandList)
8✔
502
        {
503
            const int iSrcIdx = nBand - 1;
4✔
504
            oNewArray.Add(oArray[iSrcIdx]);
4✔
505
        }
506
        const auto childName(obj.GetName());
8✔
507
        container.Delete(childName);
4✔
508
        container.Add(childName, oNewArray);
4✔
509
    }
510
}
4✔
511

512
static CPLString
513
EditISIS3MetadataForBandChange(const char *pszJSON, int nSrcBandCount,
1✔
514
                               const GDALTranslateOptions *psOptions)
515
{
516
    CPLJSONDocument oJSONDocument;
2✔
517
    const GByte *pabyData = reinterpret_cast<const GByte *>(pszJSON);
1✔
518
    if (!oJSONDocument.LoadMemory(pabyData))
1✔
519
    {
520
        return CPLString();
×
521
    }
522

523
    auto oRoot = oJSONDocument.GetRoot();
2✔
524
    if (!oRoot.IsValid())
1✔
525
    {
526
        return CPLString();
×
527
    }
528

529
    auto oBandBin = oRoot.GetObj("IsisCube/BandBin");
2✔
530
    if (oBandBin.IsValid() && oBandBin.GetType() == CPLJSONObject::Type::Object)
1✔
531
    {
532
        // Backup original BandBin object
533
        oRoot.GetObj("IsisCube").Add("OriginalBandBin", Clone(oBandBin));
1✔
534

535
        // Iterate over BandBin members and reorder/resize its arrays that
536
        // have the same number of elements than the number of bands of the
537
        // source dataset.
538
        for (auto &child : oBandBin.GetChildren())
7✔
539
        {
540
            if (child.GetType() == CPLJSONObject::Type::Array)
6✔
541
            {
542
                ReworkArray(oBandBin, child, nSrcBandCount, psOptions);
3✔
543
            }
544
            else if (child.GetType() == CPLJSONObject::Type::Object)
3✔
545
            {
546
                auto oValue = child.GetObj("value");
3✔
547
                auto oUnit = child.GetObj("unit");
3✔
548
                if (oValue.GetType() == CPLJSONObject::Type::Array)
1✔
549
                {
550
                    ReworkArray(child, oValue, nSrcBandCount, psOptions);
1✔
551
                }
552
            }
553
        }
554
    }
555

556
    return oRoot.Format(CPLJSONObject::PrettyFormat::Pretty);
2✔
557
}
558

559
/************************************************************************/
560
/*                             GDALTranslate()                          */
561
/************************************************************************/
562

563
/* clang-format off */
564
/**
565
 * Converts raster data between different formats.
566
 *
567
 * This is the equivalent of the
568
 * <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
569
 *
570
 * GDALTranslateOptions* must be allocated and freed with
571
 * GDALTranslateOptionsNew() and GDALTranslateOptionsFree() respectively.
572
 *
573
 * @param pszDest the destination dataset path.
574
 * @param hSrcDataset the source dataset handle.
575
 * @param psOptionsIn the options struct returned by GDALTranslateOptionsNew()
576
 * or NULL.
577
 * @param pbUsageError pointer to a integer output variable to store if any
578
 * usage error has occurred or NULL.
579
 * @return the output dataset (new dataset that must be closed using
580
 * GDALClose()) or NULL in case of error. If the output
581
 * format is a VRT dataset, then the returned VRT dataset has a reference to
582
 * hSrcDataset. Hence hSrcDataset should be closed after the returned dataset
583
 * if using GDALClose().
584
 * A safer alternative is to use GDALReleaseDataset() instead of using
585
 * GDALClose(), in which case you can close datasets in any order.
586
 *
587
 * @since GDAL 2.1
588
 */
589
/* clang-format on */
590

591
GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
2,714✔
592
                           const GDALTranslateOptions *psOptionsIn,
593
                           int *pbUsageError)
594

595
{
596
    CPLErrorReset();
2,714✔
597
    if (hSrcDataset == nullptr)
2,714✔
598
    {
599
        CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
×
600

601
        if (pbUsageError)
×
602
            *pbUsageError = TRUE;
×
603
        return nullptr;
×
604
    }
605
    if (pszDest == nullptr)
2,714✔
606
    {
607
        CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
×
608

609
        if (pbUsageError)
×
610
            *pbUsageError = TRUE;
×
611
        return nullptr;
×
612
    }
613

614
    auto psOptions = psOptionsIn
615
                         ? std::make_unique<GDALTranslateOptions>(*psOptionsIn)
616
                         : std::unique_ptr<GDALTranslateOptions>(
617
                               GDALTranslateOptionsNew(nullptr, nullptr));
5,428✔
618

619
    GDALDatasetH hOutDS = nullptr;
2,714✔
620
    bool bGotBounds = false;
2,714✔
621
    bool bGotGeoTransform = false;
2,714✔
622

623
    if (pbUsageError)
2,714✔
624
        *pbUsageError = FALSE;
2,133✔
625

626
    if (psOptions->adfULLR[0] != 0.0 || psOptions->adfULLR[1] != 0.0 ||
5,413✔
627
        psOptions->adfULLR[2] != 0.0 || psOptions->adfULLR[3] != 0.0)
5,413✔
628
        bGotBounds = true;
19✔
629

630
    if (psOptions->gt != GDALGeoTransform(0, 0, 0, 0, 0, 0))
2,714✔
631
        bGotGeoTransform = true;
3✔
632

633
    GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDataset);
2,714✔
634
    const char *pszSource = poSrcDS->GetDescription();
2,714✔
635

636
    if (strcmp(pszSource, pszDest) == 0 && pszSource[0] != '\0' &&
2,714✔
637
        poSrcDS->GetDriver() != GDALGetDriverByName("MEM"))
×
638
    {
639
        CPLError(CE_Failure, CPLE_AppDefined,
×
640
                 "Source and destination datasets must be different.");
641

642
        if (pbUsageError)
×
643
            *pbUsageError = TRUE;
×
644
        return nullptr;
×
645
    }
646

647
    CPLString osProjSRS;
5,428✔
648

649
    if (!psOptions->osProjSRS.empty())
2,714✔
650
    {
651
        OGRSpatialReference oSRS;
8✔
652
        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
8✔
653

654
        if (oSRS.SetFromUserInput(psOptions->osProjSRS.c_str()) != OGRERR_NONE)
8✔
655
        {
656
            CPLError(CE_Failure, CPLE_AppDefined,
×
657
                     "Failed to process SRS definition: %s",
658
                     psOptions->osProjSRS.c_str());
×
659
            return nullptr;
×
660
        }
661

662
        char *pszSRS = nullptr;
8✔
663
        oSRS.exportToWkt(&pszSRS);
8✔
664
        if (pszSRS)
8✔
665
            osProjSRS = pszSRS;
8✔
666
        CPLFree(pszSRS);
8✔
667
    }
668

669
    if (!psOptions->osOutputSRS.empty() && psOptions->osOutputSRS != "null" &&
2,823✔
670
        psOptions->osOutputSRS != "none")
109✔
671
    {
672
        OGRSpatialReference oOutputSRS;
108✔
673
        if (oOutputSRS.SetFromUserInput(psOptions->osOutputSRS.c_str()) !=
108✔
674
            OGRERR_NONE)
675
        {
676
            CPLError(CE_Failure, CPLE_AppDefined,
×
677
                     "Failed to process SRS definition: %s",
678
                     psOptions->osOutputSRS.c_str());
×
679
            return nullptr;
×
680
        }
681
    }
682

683
    /* -------------------------------------------------------------------- */
684
    /*      Check that incompatible options are not used                    */
685
    /* -------------------------------------------------------------------- */
686

687
    if ((psOptions->nOXSizePixel != 0 || psOptions->dfOXSizePct != 0.0 ||
5,232✔
688
         psOptions->nOYSizePixel != 0 || psOptions->dfOYSizePct != 0.0) &&
5,430✔
689
        (psOptions->dfXRes != 0 && psOptions->dfYRes != 0))
228✔
690
    {
691
        CPLError(CE_Failure, CPLE_IllegalArg,
×
692
                 "-outsize and -tr options cannot be used at the same time.");
693
        if (pbUsageError)
×
694
            *pbUsageError = TRUE;
×
695
        return nullptr;
×
696
    }
697
    if ((bGotBounds | bGotGeoTransform) &&
2,736✔
698
        (psOptions->dfXRes != 0 && psOptions->dfYRes != 0))
22✔
699
    {
700
        CPLError(
×
701
            CE_Failure, CPLE_IllegalArg,
702
            "-a_ullr or -a_gt options cannot be used at the same time as -tr.");
703
        if (pbUsageError)
×
704
            *pbUsageError = TRUE;
×
705
        return nullptr;
×
706
    }
707
    if (bGotBounds && bGotGeoTransform)
2,714✔
708
    {
709
        CPLError(CE_Failure, CPLE_IllegalArg,
×
710
                 "-a_ullr and -a_gt options cannot be used at the same time.");
711
        if (pbUsageError)
×
712
            *pbUsageError = TRUE;
×
713
        return nullptr;
×
714
    }
715

716
    /* -------------------------------------------------------------------- */
717
    /*      Collect some information from the source file.                  */
718
    /* -------------------------------------------------------------------- */
719
    if (psOptions->srcWin.dfXSize == 0 && psOptions->srcWin.dfYSize == 0)
2,714✔
720
    {
721
        psOptions->srcWin.dfXSize = poSrcDS->GetRasterXSize();
1,805✔
722
        psOptions->srcWin.dfYSize = poSrcDS->GetRasterYSize();
1,805✔
723
    }
724

725
    /* -------------------------------------------------------------------- */
726
    /*      Build band list to translate                                    */
727
    /* -------------------------------------------------------------------- */
728
    bool bAllBandsInOrder = true;
2,714✔
729

730
    if (psOptions->anBandList.empty())
2,714✔
731
    {
732

733
        psOptions->nBandCount = poSrcDS->GetRasterCount();
2,463✔
734
        if ((psOptions->nBandCount == 0) && (psOptions->bStrict))
2,463✔
735
        {
736
            // if not strict then the driver can fail if it doesn't support zero
737
            // bands
738
            CPLError(CE_Failure, CPLE_AppDefined,
×
739
                     "Input file has no bands, and so cannot be translated.");
740
            return nullptr;
×
741
        }
742

743
        psOptions->anBandList.resize(psOptions->nBandCount);
2,463✔
744
        for (int i = 0; i < psOptions->nBandCount; i++)
73,094✔
745
            psOptions->anBandList[i] = i + 1;
70,631✔
746
    }
747
    else
748
    {
749
        for (int i = 0; i < psOptions->nBandCount; i++)
860✔
750
        {
751
            if (std::abs(psOptions->anBandList[i]) > poSrcDS->GetRasterCount())
609✔
752
            {
753
                CPLError(CE_Failure, CPLE_AppDefined,
×
754
                         "Band %d requested, but only bands 1 to %d available.",
755
                         std::abs(psOptions->anBandList[i]),
×
756
                         poSrcDS->GetRasterCount());
757
                return nullptr;
×
758
            }
759

760
            if (psOptions->anBandList[i] != i + 1)
609✔
761
                bAllBandsInOrder = FALSE;
108✔
762
        }
763

764
        if (psOptions->nBandCount != poSrcDS->GetRasterCount())
251✔
765
            bAllBandsInOrder = FALSE;
196✔
766
    }
767

768
    if (static_cast<int>(psOptions->asScaleParams.size()) >
2,714✔
769
        psOptions->nBandCount)
2,714✔
770
    {
771
        if (!psOptions->bHasUsedExplicitScaleBand)
×
772
            CPLError(CE_Failure, CPLE_IllegalArg,
×
773
                     "-scale has been specified more times than the number of "
774
                     "output bands");
775
        else
776
            CPLError(CE_Failure, CPLE_IllegalArg,
×
777
                     "-scale_XX has been specified with XX greater than the "
778
                     "number of output bands");
779
        if (pbUsageError)
×
780
            *pbUsageError = TRUE;
×
781
        return nullptr;
×
782
    }
783

784
    if (static_cast<int>(psOptions->adfExponent.size()) > psOptions->nBandCount)
2,714✔
785
    {
786
        if (!psOptions->bHasUsedExplicitExponentBand)
×
787
            CPLError(CE_Failure, CPLE_IllegalArg,
×
788
                     "-exponent has been specified more times than the number "
789
                     "of output bands");
790
        else
791
            CPLError(CE_Failure, CPLE_IllegalArg,
×
792
                     "-exponent_XX has been specified with XX greater than the "
793
                     "number of output bands");
794
        if (pbUsageError)
×
795
            *pbUsageError = TRUE;
×
796
        return nullptr;
×
797
    }
798

799
    if (!psOptions->bQuiet && (psOptions->bSetScale || psOptions->bSetOffset) &&
2,721✔
800
        psOptions->bUnscale)
7✔
801
    {
802
        // Cf https://github.com/OSGeo/gdal/issues/7863
803
        CPLError(CE_Warning, CPLE_AppDefined,
1✔
804
                 "-a_scale/-a_offset are not applied by -unscale, but are set "
805
                 "after it, and -unscale uses the original source band "
806
                 "scale/offset values. "
807
                 "You may want to use -scale 0 1 %.16g %.16g instead. "
808
                 "This warning will not appear if -q is specified.",
809
                 psOptions->dfOffset, psOptions->dfOffset + psOptions->dfScale);
1✔
810
    }
811

812
    /* -------------------------------------------------------------------- */
813
    /*      Compute the source window from the projected source window      */
814
    /*      if the projected coordinates were provided.  Note that the      */
815
    /*      projected coordinates are in ulx, uly, lrx, lry format,         */
816
    /*      while the adfSrcWin is xoff, yoff, xsize, ysize with the        */
817
    /*      xoff,yoff being the ulx, uly in pixel/line.                     */
818
    /* -------------------------------------------------------------------- */
819
    const char *pszProjection = nullptr;
2,714✔
820

821
    if (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
5,247✔
822
        psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
5,247✔
823
    {
824
        GDALGeoTransform gt;
189✔
825
        poSrcDS->GetGeoTransform(gt);
189✔
826

827
        if (gt[1] == 0.0 || gt[5] == 0.0)
189✔
828
        {
829
            CPLError(CE_Failure, CPLE_AppDefined,
×
830
                     "The -projwin option was used, but the geotransform is "
831
                     "invalid.");
832
            return nullptr;
1✔
833
        }
834
        if (gt[2] != 0.0 || gt[4] != 0.0)
189✔
835
        {
836
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
837
                     "The -projwin option was used, but the geotransform is\n"
838
                     "rotated.  This configuration is not supported.");
839
            return nullptr;
1✔
840
        }
841

842
        if (!osProjSRS.empty())
188✔
843
        {
844
            pszProjection = poSrcDS->GetProjectionRef();
7✔
845
            if (pszProjection != nullptr && strlen(pszProjection) > 0)
7✔
846
            {
847
                OGRSpatialReference oSRSIn;
6✔
848
                OGRSpatialReference oSRSDS;
6✔
849
                oSRSIn.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
6✔
850
                oSRSDS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
6✔
851
                oSRSIn.SetFromUserInput(osProjSRS);
6✔
852
                oSRSDS.SetFromUserInput(pszProjection);
6✔
853
                if (!oSRSIn.IsSame(&oSRSDS))
6✔
854
                {
855
                    OGRCoordinateTransformation *poCT =
856
                        OGRCreateCoordinateTransformation(&oSRSIn, &oSRSDS);
5✔
857
                    if (!(poCT &&
10✔
858
                          poCT->TransformBounds(
5✔
859
                              psOptions->dfULX, psOptions->dfLRY,
5✔
860
                              psOptions->dfLRX, psOptions->dfULY,
5✔
861
                              &psOptions->dfULX, &psOptions->dfLRY,
5✔
862
                              &psOptions->dfLRX, &psOptions->dfULY, 21)))
5✔
863
                    {
864
                        OGRCoordinateTransformation::DestroyCT(poCT);
×
865

866
                        CPLError(CE_Failure, CPLE_AppDefined,
×
867
                                 "-projwin_srs ignored since coordinate "
868
                                 "transformation failed.");
869
                        return nullptr;
×
870
                    }
871
                    delete poCT;
5✔
872
                }
6✔
873
            }
874
            else
875
            {
876
                CPLError(CE_Warning, CPLE_None,
1✔
877
                         "-projwin_srs ignored since the dataset has no "
878
                         "projection.");
879
            }
880
        }
881

882
        bool bAlignToInputPixels =
883
            psOptions->osResampling.empty() ||
228✔
884
            EQUALN(psOptions->osResampling.c_str(), "NEAR", 4);
40✔
885

886
        double dfULX = psOptions->dfULX;
188✔
887
        double dfULY = psOptions->dfULY;
188✔
888

889
        psOptions->srcWin.dfXOff = (dfULX - gt[0]) / gt[1];
188✔
890
        psOptions->srcWin.dfYOff = (dfULY - gt[3]) / gt[5];
188✔
891

892
        // In case of nearest resampling, round to integer pixels (#6610)
893
        if (bAlignToInputPixels)
188✔
894
        {
895
            psOptions->srcWin.dfXOff =
306✔
896
                std::floor(psOptions->srcWin.dfXOff + 0.001);  // xoff
153✔
897
            psOptions->srcWin.dfYOff =
306✔
898
                std::floor(psOptions->srcWin.dfYOff + 0.001);  // yoff
153✔
899

900
            dfULX = psOptions->srcWin.dfXOff * gt[1] + gt[0];
153✔
901
            dfULY = psOptions->srcWin.dfYOff * gt[5] + gt[3];
153✔
902
        }
903

904
        // Calculate xsize and ysize based on the (possibly snapped) ULX, ULY
905
        psOptions->srcWin.dfXSize =
376✔
906
            (psOptions->dfLRX - dfULX) / gt[1];  // xsize
188✔
907
        psOptions->srcWin.dfYSize =
376✔
908
            (psOptions->dfLRY - dfULY) / gt[5];  // ysize
188✔
909

910
        if (bAlignToInputPixels)
188✔
911
        {
912
            psOptions->srcWin.dfXSize =
306✔
913
                std::ceil(psOptions->srcWin.dfXSize - 0.001);
153✔
914
            psOptions->srcWin.dfYSize =
153✔
915
                std::ceil(psOptions->srcWin.dfYSize - 0.001);
153✔
916
        }
917

918
        /*if( !bQuiet )
919
            fprintf( stdout,
920
                     "Computed -srcwin %g %g %g %g from projected window.\n",
921
                     srcWin.dfXOff,
922
                     srcWin.dfYOff,
923
                     srcWin.dfXSize,
924
                     srcWin.dfYSize ); */
925
    }
926

927
    /* -------------------------------------------------------------------- */
928
    /*      Verify source window dimensions.                                */
929
    /* -------------------------------------------------------------------- */
930
    if (poSrcDS->GetRasterXSize() != 0 && poSrcDS->GetRasterYSize() != 0 &&
5,425✔
931
        (psOptions->srcWin.dfXSize <= 0 || psOptions->srcWin.dfYSize <= 0))
2,712✔
932
    {
933
        CPLError(
1✔
934
            CE_Failure, CPLE_AppDefined,
935
            "Error: %s-srcwin %g %g %g %g has negative width and/or height.",
936
            (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
1✔
937
             psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
×
938
                ? "Computed "
939
                : "",
940
            psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
1✔
941
            psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize);
1✔
942
        return nullptr;
1✔
943
    }
944

945
    /* -------------------------------------------------------------------- */
946
    /*      Verify source window dimensions.                                */
947
    /* -------------------------------------------------------------------- */
948
    else if (psOptions->srcWin.dfXOff <= -1 || psOptions->srcWin.dfYOff <= -1 ||
5,408✔
949
             psOptions->srcWin.dfXOff + psOptions->srcWin.dfXSize - 1 >=
2,696✔
950
                 poSrcDS->GetRasterXSize() ||
8,085✔
951
             psOptions->srcWin.dfYOff + psOptions->srcWin.dfYSize - 1 >=
2,677✔
952
                 poSrcDS->GetRasterYSize())
2,677✔
953
    {
954
        const bool bCompletelyOutside =
955
            psOptions->srcWin.dfXOff + psOptions->srcWin.dfXSize <= 0 ||
39✔
956
            psOptions->srcWin.dfYOff + psOptions->srcWin.dfYSize <= 0 ||
33✔
957
            psOptions->srcWin.dfXOff >= poSrcDS->GetRasterXSize() ||
104✔
958
            psOptions->srcWin.dfYOff >= poSrcDS->GetRasterYSize();
32✔
959
        const bool bIsError =
960
            psOptions->bErrorOnPartiallyOutside ||
43✔
961
            (bCompletelyOutside && psOptions->bErrorOnCompletelyOutside);
4✔
962
        if (!psOptions->bQuiet || bIsError)
39✔
963
        {
964
            CPLErr eErr = bIsError ? CE_Failure : CE_Warning;
22✔
965

966
            CPLError(eErr, CPLE_AppDefined,
44✔
967
                     "%s-srcwin %g %g %g %g falls %s outside source raster "
968
                     "extent.%s",
969
                     (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
22✔
970
                      psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
9✔
971
                         ? "Computed "
972
                         : "",
973
                     psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
22✔
974
                     psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize,
22✔
975
                     bCompletelyOutside ? "completely" : "partially",
976
                     bIsError
977
                         ? ""
978
                         : " Pixels outside the source raster extent will be "
979
                           "set to the NoData value (if defined), or zero.");
980
        }
981
        if (bIsError)
39✔
982
        {
983
            return nullptr;
9✔
984
        }
985
    }
986

987
    /* -------------------------------------------------------------------- */
988
    /*      Find the output driver.                                         */
989
    /* -------------------------------------------------------------------- */
990
    if (psOptions->osFormat.empty())
2,703✔
991
    {
992
        psOptions->osFormat = GetOutputDriverForRaster(pszDest);
1,384✔
993
        if (psOptions->osFormat.empty())
1,384✔
994
        {
995
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
996
                     "Could not identify an output driver for %s", pszDest);
997
            return nullptr;
1✔
998
        }
999
    }
1000

1001
    GDALDriverH hDriver = GDALGetDriverByName(psOptions->osFormat.c_str());
2,702✔
1002
    if (hDriver == nullptr)
2,702✔
1003
    {
1004
        CPLError(CE_Failure, CPLE_IllegalArg,
1✔
1005
                 "Output driver `%s' not recognised.",
1006
                 psOptions->osFormat.c_str());
1✔
1007
        return nullptr;
1✔
1008
    }
1009

1010
    /* -------------------------------------------------------------------- */
1011
    /*      Make sure we cleanup if there is an existing dataset of this    */
1012
    /*      name.  But even if that seems to fail we will continue since    */
1013
    /*      it might just be a corrupt file or something.                   */
1014
    /*      This is needed for                                              */
1015
    /*      gdal_translate foo.tif foo.tif.ovr -outsize 50% 50%             */
1016
    /* -------------------------------------------------------------------- */
1017
    if (psOptions->aosCreateOptions.FetchBool("APPEND_SUBDATASET", false))
2,701✔
1018
    {
1019
        if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE_SUBDATASETS,
7✔
1020
                                nullptr) == nullptr)
7✔
1021
        {
1022
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
1023
                     "Subdataset creation not supported for driver %s",
1024
                     GDALGetDescription(hDriver));
1025
            return nullptr;
1✔
1026
        }
1027
    }
1028
    else
1029
    {
1030
        if (!EQUAL(psOptions->osFormat.c_str(), "VRT"))
2,694✔
1031
        {
1032
            // Prevent GDALDriver::CreateCopy() from doing that again.
1033
            psOptions->aosCreateOptions.SetNameValue(
2,240✔
1034
                "@QUIET_DELETE_ON_CREATE_COPY", "NO");
2,240✔
1035
        }
1036

1037
        if (psOptions->bNoOverwrite && !EQUAL(pszDest, ""))
2,694✔
1038
        {
1039
            VSIStatBufL sStat;
1040
            if (VSIStatL(pszDest, &sStat) == 0)
82✔
1041
            {
1042
                CPLError(CE_Failure, CPLE_AppDefined,
×
1043
                         "File '%s' already exists. Specify the --overwrite "
1044
                         "option to overwrite it.",
1045
                         pszDest);
1046
                return nullptr;
×
1047
            }
1048
            else if (std::unique_ptr<GDALDataset>(GDALDataset::Open(pszDest)))
82✔
1049
            {
1050
                CPLError(CE_Failure, CPLE_AppDefined,
×
1051
                         "Dataset '%s' already exists. Specify the --overwrite "
1052
                         "option to overwrite it.",
1053
                         pszDest);
1054
                return nullptr;
×
1055
            }
1056
        }
1057

1058
        GDALDriver::FromHandle(hDriver)->QuietDeleteForCreateCopy(pszDest,
2,694✔
1059
                                                                  poSrcDS);
1060

1061
        // Make sure to load early overviews, so that on the GTiff driver
1062
        // external .ovr is looked for before it might be created as the
1063
        // output dataset !
1064
        if (poSrcDS->GetRasterCount())
2,694✔
1065
        {
1066
            auto poBand = poSrcDS->GetRasterBand(1);
2,691✔
1067
            if (poBand)
2,691✔
1068
                poBand->GetOverviewCount();
2,691✔
1069
        }
1070
    }
1071

1072
    char **papszDriverMD = GDALGetMetadata(hDriver, nullptr);
2,700✔
1073

1074
    if (!CPLTestBool(
2,700✔
1075
            CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER, "FALSE")))
1076
    {
1077
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1078
                 "%s driver has no raster capabilities.",
1079
                 psOptions->osFormat.c_str());
1✔
1080
        return nullptr;
1✔
1081
    }
1082

1083
    if (!CPLTestBool(
2,699✔
1084
            CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")) &&
2,878✔
1085
        !CPLTestBool(
179✔
1086
            CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATECOPY, "FALSE")))
1087
    {
1088
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1089
                 "%s driver has no creation capabilities.",
1090
                 psOptions->osFormat.c_str());
1✔
1091
        return nullptr;
1✔
1092
    }
1093

1094
    /* -------------------------------------------------------------------- */
1095
    /*      The short form is to CreateCopy().  We use this if the input    */
1096
    /*      matches the whole dataset.  Eventually we should rewrite        */
1097
    /*      this entire program to use virtual datasets to construct a      */
1098
    /*      virtual input source to copy from.                              */
1099
    /* -------------------------------------------------------------------- */
1100

1101
    const bool bKeepResolution =
1102
        psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
5,200✔
1103
        psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0 &&
7,670✔
1104
        psOptions->dfXRes == 0.0;
2,470✔
1105
    const bool bSpatialArrangementPreserved =
1106
        psOptions->srcWin.dfXOff == 0 && psOptions->srcWin.dfYOff == 0 &&
4,517✔
1107
        psOptions->srcWin.dfXSize == poSrcDS->GetRasterXSize() &&
1,748✔
1108
        psOptions->srcWin.dfYSize == poSrcDS->GetRasterYSize() &&
4,517✔
1109
        bKeepResolution;
2,698✔
1110

1111
    if (psOptions->eOutputType == GDT_Unknown &&
5,137✔
1112
        psOptions->asScaleParams.empty() && psOptions->adfExponent.empty() &&
4,831✔
1113
        !psOptions->bUnscale && !psOptions->bSetScale &&
2,391✔
1114
        !psOptions->bSetOffset && psOptions->aosMetadataOptions.empty() &&
2,386✔
1115
        psOptions->aosDomainMetadataOptions.empty() && bAllBandsInOrder &&
2,357✔
1116
        psOptions->eMaskMode == MASK_AUTO && bSpatialArrangementPreserved &&
2,273✔
1117
        !psOptions->bNoGCP && psOptions->asGCPs.empty() && !bGotBounds &&
1,043✔
1118
        !bGotGeoTransform && psOptions->osOutputSRS.empty() &&
1,017✔
1119
        psOptions->dfOutputCoordinateEpoch == 0 && !psOptions->bSetNoData &&
969✔
1120
        !psOptions->bUnsetNoData && psOptions->nRGBExpand == 0 &&
944✔
1121
        !psOptions->bNoRAT && psOptions->anColorInterp.empty() &&
921✔
1122
        !psOptions->bNoXMP && psOptions->nOvLevel == OVR_LEVEL_AUTO)
5,137✔
1123
    {
1124

1125
        // For gdal_translate_fuzzer
1126
        if (psOptions->nLimitOutSize > 0)
905✔
1127
        {
1128
            vsi_l_offset nRawOutSize =
1129
                static_cast<vsi_l_offset>(poSrcDS->GetRasterXSize()) *
×
1130
                poSrcDS->GetRasterYSize() * psOptions->nBandCount;
×
1131
            if (psOptions->nBandCount)
×
1132
            {
1133
                nRawOutSize *= GDALGetDataTypeSizeBytes(
×
1134
                    poSrcDS->GetRasterBand(1)->GetRasterDataType());
1135
            }
1136
            if (nRawOutSize >
×
1137
                static_cast<vsi_l_offset>(psOptions->nLimitOutSize))
×
1138
            {
1139
                CPLError(CE_Failure, CPLE_IllegalArg,
×
1140
                         "Attempt to create %dx%d dataset is above authorized "
1141
                         "limit.",
1142
                         poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1143
                return nullptr;
×
1144
            }
1145
        }
1146

1147
        /* --------------------------------------------------------------------
1148
         */
1149
        /*      Compute stats if required. */
1150
        /* --------------------------------------------------------------------
1151
         */
1152

1153
        if (psOptions->bStats && EQUAL(psOptions->osFormat.c_str(), "COG"))
905✔
1154
        {
1155
            psOptions->aosCreateOptions.SetNameValue("STATISTICS", "YES");
2✔
1156
        }
1157
        else if (psOptions->bStats)
903✔
1158
        {
1159
            for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
2✔
1160
            {
1161
                double dfMin, dfMax, dfMean, dfStdDev;
1162
                poSrcDS->GetRasterBand(i + 1)->ComputeStatistics(
1✔
1163
                    psOptions->bApproxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
1✔
1164
                    GDALDummyProgress, nullptr);
1✔
1165
            }
1166
        }
1167

1168
        hOutDS = GDALCreateCopy(
905✔
1169
            hDriver, pszDest, GDALDataset::ToHandle(poSrcDS),
1170
            psOptions->bStrict, psOptions->aosCreateOptions.List(),
905✔
1171
            psOptions->pfnProgress, psOptions->pProgressData);
905✔
1172
        hOutDS = GDALTranslateFlush(hOutDS);
905✔
1173

1174
        return hOutDS;
905✔
1175
    }
1176

1177
    if (psOptions->aosCreateOptions.FetchNameValue("COPY_SRC_OVERVIEWS"))
1,793✔
1178
    {
1179
        CPLError(CE_Warning, CPLE_AppDefined,
×
1180
                 "General options of gdal_translate make the "
1181
                 "COPY_SRC_OVERVIEWS creation option ineffective as they hide "
1182
                 "the overviews");
1183
    }
1184

1185
    /* -------------------------------------------------------------------- */
1186
    /*      Establish some parameters.                                      */
1187
    /* -------------------------------------------------------------------- */
1188
    int nOXSize = 0;
1,793✔
1189
    int nOYSize = 0;
1,793✔
1190

1191
    bool bHasSrcGeoTransform = false;
1,793✔
1192
    GDALGeoTransform srcGT;
1,793✔
1193
    if (poSrcDS->GetGeoTransform(srcGT) == CE_None)
1,793✔
1194
        bHasSrcGeoTransform = true;
1,554✔
1195

1196
    const bool bOutsizeExplicitlySet =
1197
        !(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
3,360✔
1198
          psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0);
1,567✔
1199
    if (psOptions->dfXRes != 0.0 && psOptions->dfYRes != 0.0)
1,793✔
1200
    {
1201
        if (!(bHasSrcGeoTransform && psOptions->asGCPs.empty() &&
24✔
1202
              srcGT[2] == 0.0 && srcGT[4] == 0.0))
12✔
1203
        {
1204
            CPLError(CE_Failure, CPLE_IllegalArg,
×
1205
                     "The -tr option was used, but there's no geotransform or "
1206
                     "it is\n"
1207
                     "rotated.  This configuration is not supported.");
1208
            return nullptr;
×
1209
        }
1210
        const double dfOXSize =
1211
            psOptions->srcWin.dfXSize / psOptions->dfXRes * srcGT[1] + 0.5;
12✔
1212
        const double dfOYSize =
1213
            psOptions->srcWin.dfYSize / psOptions->dfYRes * fabs(srcGT[5]) +
12✔
1214
            0.5;
12✔
1215
        if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
12✔
1216
            dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
24✔
1217
        {
1218
            CPLError(CE_Failure, CPLE_IllegalArg,
×
1219
                     "Invalid output size: %g x %g", dfOXSize, dfOYSize);
1220
            return nullptr;
×
1221
        }
1222
        nOXSize = static_cast<int>(dfOXSize);
12✔
1223
        nOYSize = static_cast<int>(dfOYSize);
12✔
1224
    }
1225
    else if (!bOutsizeExplicitlySet)
1,781✔
1226
    {
1227
        double dfOXSize = ceil(psOptions->srcWin.dfXSize - 0.001);
1,553✔
1228
        double dfOYSize = ceil(psOptions->srcWin.dfYSize - 0.001);
1,553✔
1229
        if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
1,553✔
1230
            dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
3,106✔
1231
        {
1232
            CPLError(CE_Failure, CPLE_IllegalArg,
2✔
1233
                     "Invalid output size: %g x %g", dfOXSize, dfOYSize);
1234
            return nullptr;
2✔
1235
        }
1236
        nOXSize = static_cast<int>(dfOXSize);
1,551✔
1237
        nOYSize = static_cast<int>(dfOYSize);
1,551✔
1238
    }
1239
    else
1240
    {
1241
        if (!(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0))
228✔
1242
        {
1243
            if (psOptions->nOXSizePixel != 0)
226✔
1244
                nOXSize = psOptions->nOXSizePixel;
196✔
1245
            else
1246
            {
1247
                const double dfOXSize =
1248
                    psOptions->dfOXSizePct / 100 * psOptions->srcWin.dfXSize;
30✔
1249
                if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize))
30✔
1250
                {
1251
                    CPLError(CE_Failure, CPLE_IllegalArg,
1✔
1252
                             "Invalid output width: %g", dfOXSize);
1253
                    return nullptr;
1✔
1254
                }
1255
                nOXSize = static_cast<int>(dfOXSize);
29✔
1256
            }
1257
        }
1258

1259
        if (!(psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0))
227✔
1260
        {
1261
            if (psOptions->nOYSizePixel != 0)
161✔
1262
                nOYSize = psOptions->nOYSizePixel;
133✔
1263
            else
1264
            {
1265
                const double dfOYSize =
1266
                    psOptions->dfOYSizePct / 100 * psOptions->srcWin.dfYSize;
28✔
1267
                if (dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
28✔
1268
                {
1269
                    CPLError(CE_Failure, CPLE_IllegalArg,
1✔
1270
                             "Invalid output height: %g", dfOYSize);
1271
                    return nullptr;
1✔
1272
                }
1273
                nOYSize = static_cast<int>(dfOYSize);
27✔
1274
            }
1275
        }
1276

1277
        if (psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0)
226✔
1278
        {
1279
            const double dfOXSize = static_cast<double>(nOYSize) *
4✔
1280
                                        psOptions->srcWin.dfXSize /
2✔
1281
                                        psOptions->srcWin.dfYSize +
2✔
1282
                                    0.5;
2✔
1283
            if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize))
2✔
1284
            {
1285
                CPLError(CE_Failure, CPLE_IllegalArg,
×
1286
                         "Invalid output width: %g", dfOXSize);
1287
                return nullptr;
×
1288
            }
1289
            nOXSize = static_cast<int>(dfOXSize);
2✔
1290
        }
1291
        else if (psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0)
224✔
1292
        {
1293
            const double dfOYSize = static_cast<double>(nOXSize) *
132✔
1294
                                        psOptions->srcWin.dfYSize /
66✔
1295
                                        psOptions->srcWin.dfXSize +
66✔
1296
                                    0.5;
66✔
1297
            if (dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
66✔
1298
            {
1299
                CPLError(CE_Failure, CPLE_IllegalArg,
×
1300
                         "Invalid output height: %g", dfOYSize);
1301
                return nullptr;
×
1302
            }
1303
            nOYSize = static_cast<int>(dfOYSize);
66✔
1304
        }
1305
    }
1306

1307
    if (nOXSize <= 0 || nOYSize <= 0)
1,789✔
1308
    {
1309
        CPLError(CE_Failure, CPLE_IllegalArg,
×
1310
                 "Attempt to create %dx%d dataset is illegal.", nOXSize,
1311
                 nOYSize);
1312
        return nullptr;
×
1313
    }
1314

1315
    // Build overview dataset if -ovr is specified
1316
    GDALDataset *poSrcOvrDS = nullptr;
1,789✔
1317
    GDALDataset *poSrcDSOri = poSrcDS;
1,789✔
1318
    const auto poFirstBand = poSrcDS->GetRasterBand(1);
1,789✔
1319
    const int nOvCount = poFirstBand ? poFirstBand->GetOverviewCount() : 0;
1,789✔
1320
    if (psOptions->nOvLevel < OVR_LEVEL_AUTO && poFirstBand && nOvCount > 0)
1,789✔
1321
    {
1322
        int iOvr = 0;
4✔
1323
        for (; iOvr < nOvCount - 1; iOvr++)
7✔
1324
        {
1325
            if (poFirstBand->GetOverview(iOvr)->GetXSize() <= nOXSize)
4✔
1326
            {
1327
                break;
1✔
1328
            }
1329
        }
1330
        iOvr += (psOptions->nOvLevel - OVR_LEVEL_AUTO);
4✔
1331
        if (iOvr >= 0)
4✔
1332
        {
1333
            CPLDebug("GDAL", "Selecting overview level %d", iOvr);
3✔
1334
            poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, iOvr,
3✔
1335
                                                   /* bThisLevelOnly = */ true);
1336
        }
1337
    }
1338
    else if (psOptions->nOvLevel >= OVR_LEVEL_NONE)
1,785✔
1339
    {
1340
        poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, psOptions->nOvLevel,
11✔
1341
                                               /* bThisLevelOnly = */ true);
1342
        if (poSrcOvrDS == nullptr)
11✔
1343
        {
1344
            if (!psOptions->bQuiet)
3✔
1345
            {
1346
                if (nOvCount > 0)
3✔
1347
                {
1348
                    CPLError(CE_Warning, CPLE_AppDefined,
2✔
1349
                             "Cannot get overview level %d. "
1350
                             "Defaulting to level %d.",
1351
                             psOptions->nOvLevel, nOvCount - 1);
2✔
1352
                }
1353
                else
1354
                {
1355
                    CPLError(CE_Warning, CPLE_AppDefined,
1✔
1356
                             "Cannot get overview level %d. "
1357
                             "Defaulting to full resolution.",
1358
                             psOptions->nOvLevel);
1✔
1359
                }
1360
            }
1361
            if (nOvCount > 0)
3✔
1362
                poSrcOvrDS =
1363
                    GDALCreateOverviewDataset(poSrcDS, nOvCount - 1,
2✔
1364
                                              /* bThisLevelOnly = */ true);
1365
        }
1366
        if (poSrcOvrDS && psOptions->dfXRes == 0.0 && !bOutsizeExplicitlySet)
11✔
1367
        {
1368
            const double dfRatioX =
1369
                static_cast<double>(poSrcDSOri->GetRasterXSize()) /
8✔
1370
                poSrcOvrDS->GetRasterXSize();
8✔
1371
            const double dfRatioY =
1372
                static_cast<double>(poSrcDSOri->GetRasterYSize()) /
8✔
1373
                poSrcOvrDS->GetRasterYSize();
8✔
1374
            nOXSize =
8✔
1375
                std::max(1, static_cast<int>(ceil(nOXSize / dfRatioX - 0.001)));
8✔
1376
            nOYSize =
8✔
1377
                std::max(1, static_cast<int>(ceil(nOYSize / dfRatioY - 0.001)));
8✔
1378
        }
1379
    }
1380

1381
    if (poSrcOvrDS)
1,789✔
1382
        poSrcDS = poSrcOvrDS;
13✔
1383
    else
1384
        poSrcDS->Reference();
1,776✔
1385

1386
    // For gdal_translate_fuzzer
1387
    if (psOptions->nLimitOutSize > 0)
1,789✔
1388
    {
1389
        vsi_l_offset nRawOutSize = static_cast<vsi_l_offset>(nOXSize) * nOYSize;
×
1390
        if (psOptions->nBandCount)
×
1391
        {
1392
            if (nRawOutSize > std::numeric_limits<vsi_l_offset>::max() /
×
1393
                                  psOptions->nBandCount)
×
1394
            {
1395
                poSrcDS->Release();
×
1396
                return nullptr;
×
1397
            }
1398
            nRawOutSize *= psOptions->nBandCount;
×
1399
            const int nDTSize = GDALGetDataTypeSizeBytes(
×
1400
                poSrcDS->GetRasterBand(1)->GetRasterDataType());
1401
            if (nDTSize > 0 &&
×
1402
                nRawOutSize >
1403
                    std::numeric_limits<vsi_l_offset>::max() / nDTSize)
×
1404
            {
1405
                poSrcDS->Release();
×
1406
                return nullptr;
×
1407
            }
1408
            nRawOutSize *= nDTSize;
×
1409
        }
1410
        if (nRawOutSize > static_cast<vsi_l_offset>(psOptions->nLimitOutSize))
×
1411
        {
1412
            CPLError(
×
1413
                CE_Failure, CPLE_IllegalArg,
1414
                "Attempt to create %dx%d dataset is above authorized limit.",
1415
                nOXSize, nOYSize);
1416
            poSrcDS->Release();
×
1417
            return nullptr;
×
1418
        }
1419
    }
1420

1421
    /* ==================================================================== */
1422
    /*      Create a virtual dataset.                                       */
1423
    /* ==================================================================== */
1424

1425
    /* -------------------------------------------------------------------- */
1426
    /*      Make a virtual clone.                                           */
1427
    /* -------------------------------------------------------------------- */
1428
    VRTDataset *poVDS = static_cast<VRTDataset *>(VRTCreate(nOXSize, nOYSize));
1,789✔
1429

1430
    if (psOptions->asGCPs.empty())
1,789✔
1431
    {
1432
        if (psOptions->osOutputSRS == "null" ||
3,560✔
1433
            psOptions->osOutputSRS == "none")
1,780✔
1434
        {
1435
            poVDS->SetSpatialRef(nullptr);
1✔
1436
        }
1437
        else
1438
        {
1439
            OGRSpatialReference oSRS;
3,558✔
1440
            if (!psOptions->osOutputSRS.empty())
1,779✔
1441
            {
1442
                oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
103✔
1443
                oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
103✔
1444
            }
1445
            else
1446
            {
1447
                const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
1,676✔
1448
                if (poSrcSRS)
1,676✔
1449
                    oSRS = *poSrcSRS;
1,346✔
1450
            }
1451
            if (!oSRS.IsEmpty())
1,779✔
1452
            {
1453
                if (psOptions->dfOutputCoordinateEpoch > 0)
1,449✔
1454
                    oSRS.SetCoordinateEpoch(psOptions->dfOutputCoordinateEpoch);
4✔
1455
                poVDS->SetSpatialRef(&oSRS);
1,449✔
1456
            }
1457
        }
1458
    }
1459

1460
    bool bHasDstGeoTransform = false;
1,789✔
1461
    GDALGeoTransform dstGT;
1,789✔
1462

1463
    if (bGotBounds)
1,789✔
1464
    {
1465
        bHasDstGeoTransform = true;
19✔
1466
        dstGT[0] = psOptions->adfULLR[0];
19✔
1467
        dstGT[1] = (psOptions->adfULLR[2] - psOptions->adfULLR[0]) / nOXSize;
19✔
1468
        dstGT[2] = 0.0;
19✔
1469
        dstGT[3] = psOptions->adfULLR[1];
19✔
1470
        dstGT[4] = 0.0;
19✔
1471
        dstGT[5] = (psOptions->adfULLR[3] - psOptions->adfULLR[1]) / nOYSize;
19✔
1472

1473
        poVDS->SetGeoTransform(dstGT);
19✔
1474
    }
1475

1476
    else if (bGotGeoTransform)
1,770✔
1477
    {
1478
        bHasDstGeoTransform = true;
3✔
1479
        poVDS->SetGeoTransform(psOptions->gt);
3✔
1480
    }
1481

1482
    else if (bHasSrcGeoTransform && psOptions->asGCPs.empty())
1,767✔
1483
    {
1484
        bHasDstGeoTransform = true;
1,526✔
1485
        dstGT = srcGT;
1,526✔
1486
        dstGT[0] += psOptions->srcWin.dfXOff * dstGT[1] +
1,526✔
1487
                    psOptions->srcWin.dfYOff * dstGT[2];
1,526✔
1488
        dstGT[3] += psOptions->srcWin.dfXOff * dstGT[4] +
1,526✔
1489
                    psOptions->srcWin.dfYOff * dstGT[5];
1,526✔
1490

1491
        const double dfXRatio = psOptions->srcWin.dfXSize / nOXSize;
1,526✔
1492
        const double dfYRatio = psOptions->srcWin.dfYSize / nOYSize;
1,526✔
1493
        dstGT.Rescale(dfXRatio, dfYRatio);
1,526✔
1494

1495
        if (psOptions->dfXRes != 0.0)
1,526✔
1496
        {
1497
            dstGT[1] = psOptions->dfXRes;
12✔
1498
            dstGT[5] = (dstGT[5] > 0) ? psOptions->dfYRes : -psOptions->dfYRes;
12✔
1499
        }
1500

1501
        poVDS->SetGeoTransform(dstGT);
1,526✔
1502
    }
1503

1504
    if (!psOptions->asGCPs.empty())
1,789✔
1505
    {
1506
        OGRSpatialReference oSRS;
18✔
1507
        if (psOptions->osOutputSRS == "null" ||
18✔
1508
            psOptions->osOutputSRS == "none")
9✔
1509
        {
1510
            // nothing to do
1511
        }
1512
        else if (!psOptions->osOutputSRS.empty())
9✔
1513
        {
1514
            oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
5✔
1515
            oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5✔
1516
        }
1517
        else
1518
        {
1519
            const OGRSpatialReference *poSrcSRS = poSrcDS->GetGCPSpatialRef();
4✔
1520
            if (poSrcSRS)
4✔
1521
                oSRS = *poSrcSRS;
×
1522
        }
1523
        poVDS->SetGCPs(static_cast<int>(psOptions->asGCPs.size()),
18✔
1524
                       gdal::GCP::c_ptr(psOptions->asGCPs),
9✔
1525
                       !oSRS.IsEmpty() ? &oSRS : nullptr);
9✔
1526
    }
1527

1528
    else if (!psOptions->bNoGCP && poSrcDSOri->GetGCPCount() > 0)
1,780✔
1529
    {
1530
        const int nGCPs = poSrcDSOri->GetGCPCount();
1✔
1531

1532
        GDAL_GCP *pasGCPs = GDALDuplicateGCPs(nGCPs, poSrcDSOri->GetGCPs());
1✔
1533

1534
        for (int i = 0; i < nGCPs; i++)
5✔
1535
        {
1536
            pasGCPs[i].dfGCPPixel -= psOptions->srcWin.dfXOff;
4✔
1537
            pasGCPs[i].dfGCPLine -= psOptions->srcWin.dfYOff;
4✔
1538
            pasGCPs[i].dfGCPPixel *=
4✔
1539
                nOXSize / static_cast<double>(psOptions->srcWin.dfXSize);
4✔
1540
            pasGCPs[i].dfGCPLine *=
4✔
1541
                nOYSize / static_cast<double>(psOptions->srcWin.dfYSize);
4✔
1542
        }
1543

1544
        poVDS->SetGCPs(nGCPs, pasGCPs, poSrcDSOri->GetGCPSpatialRef());
1✔
1545

1546
        GDALDeinitGCPs(nGCPs, pasGCPs);
1✔
1547
        CPLFree(pasGCPs);
1✔
1548
    }
1549

1550
    /* -------------------------------------------------------------------- */
1551
    /*      To make the VRT to look less awkward (but this is optional      */
1552
    /*      in fact), avoid negative values.                                */
1553
    /* -------------------------------------------------------------------- */
1554
    GDALTranslateOptions::PixelLineWindow dstWin{
1,789✔
1555
        0.0, 0.0, static_cast<double>(nOXSize), static_cast<double>(nOYSize)};
1,789✔
1556

1557
    // When specifying -tr with non-nearest resampling, make sure that the
1558
    // size of target window precisely matches the requested resolution, to
1559
    // avoid any shift.
1560
    if (bHasSrcGeoTransform && bHasDstGeoTransform &&
1,550✔
1561
        psOptions->dfXRes != 0.0 && !psOptions->osResampling.empty() &&
3,344✔
1562
        !EQUALN(psOptions->osResampling.c_str(), "NEAR", 4))
5✔
1563
    {
1564
        dstWin.dfXSize = psOptions->srcWin.dfXSize * srcGT[1] / dstGT[1];
5✔
1565
        dstWin.dfYSize = psOptions->srcWin.dfYSize * fabs(srcGT[5] / dstGT[5]);
5✔
1566
    }
1567

1568
    GDALTranslateOptions::PixelLineWindow srcWinOri(psOptions->srcWin);
1,789✔
1569
    const double dfRatioX =
1570
        poSrcDS->GetRasterXSize() == 0
1,789✔
1571
            ? 1.0
1,789✔
1572
            : static_cast<double>(poSrcDSOri->GetRasterXSize()) /
1,789✔
1573
                  poSrcDS->GetRasterXSize();
1,789✔
1574
    const double dfRatioY =
1575
        poSrcDS->GetRasterYSize() == 0
1,789✔
1576
            ? 1.0
1,789✔
1577
            : static_cast<double>(poSrcDSOri->GetRasterYSize()) /
1,789✔
1578
                  poSrcDS->GetRasterYSize();
1,789✔
1579
    psOptions->srcWin.dfXOff /= dfRatioX;
1,789✔
1580
    psOptions->srcWin.dfYOff /= dfRatioY;
1,789✔
1581
    psOptions->srcWin.dfXSize /= dfRatioX;
1,789✔
1582
    psOptions->srcWin.dfYSize /= dfRatioY;
1,789✔
1583
    FixSrcDstWindow(psOptions->srcWin, dstWin, poSrcDS->GetRasterXSize(),
1,789✔
1584
                    poSrcDS->GetRasterYSize());
1585

1586
    /* -------------------------------------------------------------------- */
1587
    /*      Transfer generally applicable metadata.                         */
1588
    /* -------------------------------------------------------------------- */
1589
    char **papszMetadata = CSLDuplicate(poSrcDS->GetMetadata());
1,789✔
1590
    if (!psOptions->asScaleParams.empty() || psOptions->bUnscale ||
3,494✔
1591
        psOptions->eOutputType != GDT_Unknown)
1,705✔
1592
    {
1593
        /* Remove TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */
1594
        /* if the data range may change because of options */
1595
        char **papszIter = papszMetadata;
308✔
1596
        while (papszIter && *papszIter)
679✔
1597
        {
1598
            if (STARTS_WITH_CI(*papszIter, "TIFFTAG_MINSAMPLEVALUE=") ||
371✔
1599
                STARTS_WITH_CI(*papszIter, "TIFFTAG_MAXSAMPLEVALUE="))
371✔
1600
            {
1601
                CPLFree(*papszIter);
×
1602
                memmove(papszIter, papszIter + 1,
×
1603
                        sizeof(char *) * (CSLCount(papszIter + 1) + 1));
×
1604
            }
1605
            else
1606
                papszIter++;
371✔
1607
        }
1608
    }
1609

1610
    // Remove NITF_BLOCKA_ stuff if georeferencing is changed
1611
    if (!(psOptions->srcWin.dfXOff == 0 && psOptions->srcWin.dfYOff == 0 &&
2,635✔
1612
          psOptions->srcWin.dfXSize == poSrcDS->GetRasterXSize() &&
846✔
1613
          psOptions->srcWin.dfYSize == poSrcDS->GetRasterYSize() &&
758✔
1614
          psOptions->asGCPs.empty() && !bGotBounds && !bGotGeoTransform))
757✔
1615
    {
1616
        char **papszIter = papszMetadata;
1,063✔
1617
        while (papszIter && *papszIter)
3,022✔
1618
        {
1619
            if (STARTS_WITH_CI(*papszIter, "NITF_BLOCKA_"))
1,959✔
1620
            {
1621
                CPLFree(*papszIter);
10✔
1622
                memmove(papszIter, papszIter + 1,
10✔
1623
                        sizeof(char *) * (CSLCount(papszIter + 1) + 1));
10✔
1624
            }
1625
            else
1626
                papszIter++;
1,949✔
1627
        }
1628
    }
1629

1630
    {
1631
        char **papszIter = papszMetadata;
1,789✔
1632
        while (papszIter && *papszIter)
4,616✔
1633
        {
1634
            // Do not preserve the CACHE_PATH from the WMS driver
1635
            if (STARTS_WITH_CI(*papszIter, "CACHE_PATH="))
2,827✔
1636
            {
1637
                CPLFree(*papszIter);
×
1638
                memmove(papszIter, papszIter + 1,
×
1639
                        sizeof(char *) * (CSLCount(papszIter + 1) + 1));
×
1640
            }
1641
            else
1642
                papszIter++;
2,827✔
1643
        }
1644
    }
1645

1646
    if (CSLFetchNameValue(papszMetadata, "NODATA_VALUES") &&
1,794✔
1647
        !(bAllBandsInOrder &&
5✔
1648
          psOptions->nBandCount == poSrcDS->GetRasterCount()))
1✔
1649
    {
1650
        papszMetadata =
1651
            CSLSetNameValue(papszMetadata, "NODATA_VALUES", nullptr);
4✔
1652
    }
1653

1654
    poVDS->SetMetadata(papszMetadata);
1,789✔
1655
    CSLDestroy(papszMetadata);
1,789✔
1656
    AttachMetadata(GDALDataset::ToHandle(poVDS), psOptions->aosMetadataOptions);
1,789✔
1657

1658
    AttachDomainMetadata(GDALDataset::ToHandle(poVDS),
1,789✔
1659
                         psOptions->aosDomainMetadataOptions);
1,789✔
1660

1661
    const char *pszInterleave =
1662
        poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
1,789✔
1663
    if (pszInterleave)
1,789✔
1664
        poVDS->SetMetadataItem("INTERLEAVE", pszInterleave, "IMAGE_STRUCTURE");
1,613✔
1665

1666
    {
1667
        const char *pszCompression =
1668
            poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
1,789✔
1669
        if (pszCompression)
1,789✔
1670
        {
1671
            poVDS->SetMetadataItem("COMPRESSION", pszCompression,
52✔
1672
                                   "IMAGE_STRUCTURE");
52✔
1673
        }
1674
    }
1675

1676
    /* ISIS3 metadata preservation */
1677
    char **papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
1,789✔
1678
    if (papszMD_ISIS3 != nullptr)
1,789✔
1679
    {
1680
        if (!bAllBandsInOrder)
3✔
1681
        {
1682
            CPLString osJSON = EditISIS3MetadataForBandChange(
1683
                papszMD_ISIS3[0], poSrcDS->GetRasterCount(), psOptions.get());
2✔
1684
            if (!osJSON.empty())
1✔
1685
            {
1686
                char *apszMD[] = {&osJSON[0], nullptr};
1✔
1687
                poVDS->SetMetadata(apszMD, "json:ISIS3");
1✔
1688
            }
1689
        }
1690
        else
1691
        {
1692
            poVDS->SetMetadata(papszMD_ISIS3, "json:ISIS3");
2✔
1693
        }
1694
    }
1695

1696
    // PDS4 -> PDS4 special case
1697
    if (EQUAL(psOptions->osFormat.c_str(), "PDS4"))
1,789✔
1698
    {
1699
        char **papszMD_PDS4 = poSrcDS->GetMetadata("xml:PDS4");
3✔
1700
        if (papszMD_PDS4 != nullptr)
3✔
1701
            poVDS->SetMetadata(papszMD_PDS4, "xml:PDS4");
2✔
1702
    }
1703

1704
    // VICAR -> VICAR special case
1705
    if (EQUAL(psOptions->osFormat.c_str(), "VICAR"))
1,789✔
1706
    {
1707
        char **papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
×
1708
        if (papszMD_VICAR != nullptr)
×
1709
            poVDS->SetMetadata(papszMD_VICAR, "json:VICAR");
×
1710
    }
1711

1712
    // Copy XMP metadata
1713
    if (!psOptions->bNoXMP)
1,789✔
1714
    {
1715
        char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
1,787✔
1716
        if (papszXMP != nullptr && *papszXMP != nullptr)
1,787✔
1717
        {
1718
            poVDS->SetMetadata(papszXMP, "xml:XMP");
1✔
1719
        }
1720
    }
1721

1722
    /* -------------------------------------------------------------------- */
1723
    /*      Transfer metadata that remains valid if the spatial             */
1724
    /*      arrangement of the data is unaltered.                           */
1725
    /* -------------------------------------------------------------------- */
1726
    if (bSpatialArrangementPreserved)
1,789✔
1727
    {
1728
        char **papszMD = poSrcDS->GetMetadata("RPC");
540✔
1729
        if (papszMD != nullptr)
540✔
1730
            poVDS->SetMetadata(papszMD, "RPC");
2✔
1731

1732
        papszMD = poSrcDS->GetMetadata("GEOLOCATION");
540✔
1733
        if (papszMD != nullptr)
540✔
1734
            poVDS->SetMetadata(papszMD, "GEOLOCATION");
1✔
1735
    }
1736
    else
1737
    {
1738
        char **papszMD = poSrcDSOri->GetMetadata("RPC");
1,249✔
1739
        if (papszMD != nullptr)
1,249✔
1740
        {
1741
            papszMD = CSLDuplicate(papszMD);
2✔
1742

1743
            double dfSAMP_OFF =
1744
                CPLAtof(CSLFetchNameValueDef(papszMD, "SAMP_OFF", "0"));
2✔
1745
            double dfLINE_OFF =
1746
                CPLAtof(CSLFetchNameValueDef(papszMD, "LINE_OFF", "0"));
2✔
1747
            double dfSAMP_SCALE =
1748
                CPLAtof(CSLFetchNameValueDef(papszMD, "SAMP_SCALE", "1"));
2✔
1749
            double dfLINE_SCALE =
1750
                CPLAtof(CSLFetchNameValueDef(papszMD, "LINE_SCALE", "1"));
2✔
1751

1752
            dfSAMP_OFF -= srcWinOri.dfXOff;
2✔
1753
            dfLINE_OFF -= srcWinOri.dfYOff;
2✔
1754

1755
            const double df2 = srcWinOri.dfXSize;
2✔
1756
            const double df3 = srcWinOri.dfYSize;
2✔
1757
            const double dfXRatio = nOXSize / df2;
2✔
1758
            const double dfYRatio = nOYSize / df3;
2✔
1759

1760
            // For line offset and pixel offset, we need to convert from RPC
1761
            // pixel center registration convention to GDAL pixel top-left corner
1762
            // registration convention by adding an initial 0.5 shift, and un-apply
1763
            // it after scaling.
1764

1765
            dfSAMP_OFF += 0.5;
2✔
1766
            dfSAMP_OFF *= dfXRatio;
2✔
1767
            dfSAMP_OFF -= 0.5;
2✔
1768

1769
            dfLINE_OFF += 0.5;
2✔
1770
            dfLINE_OFF *= dfYRatio;
2✔
1771
            dfLINE_OFF -= 0.5;
2✔
1772

1773
            dfSAMP_SCALE *= dfXRatio;
2✔
1774
            dfLINE_SCALE *= dfYRatio;
2✔
1775

1776
            CPLString osField;
4✔
1777
            osField.Printf("%.15g", dfLINE_OFF);
2✔
1778
            papszMD = CSLSetNameValue(papszMD, "LINE_OFF", osField);
2✔
1779

1780
            osField.Printf("%.15g", dfSAMP_OFF);
2✔
1781
            papszMD = CSLSetNameValue(papszMD, "SAMP_OFF", osField);
2✔
1782

1783
            osField.Printf("%.15g", dfLINE_SCALE);
2✔
1784
            papszMD = CSLSetNameValue(papszMD, "LINE_SCALE", osField);
2✔
1785

1786
            osField.Printf("%.15g", dfSAMP_SCALE);
2✔
1787
            papszMD = CSLSetNameValue(papszMD, "SAMP_SCALE", osField);
2✔
1788

1789
            poVDS->SetMetadata(papszMD, "RPC");
2✔
1790
            CSLDestroy(papszMD);
2✔
1791
        }
1792
    }
1793

1794
    const int nSrcBandCount = psOptions->nBandCount;
1,789✔
1795

1796
    if (psOptions->nRGBExpand != 0)
1,789✔
1797
    {
1798
        GDALRasterBand *poSrcBand =
1799
            poSrcDS->GetRasterBand(std::abs(psOptions->anBandList[0]));
22✔
1800
        if (psOptions->anBandList[0] < 0)
22✔
1801
            poSrcBand = poSrcBand->GetMaskBand();
×
1802
        GDALColorTable *poColorTable = poSrcBand->GetColorTable();
22✔
1803
        if (poColorTable == nullptr)
22✔
1804
        {
1805
            CPLError(CE_Failure, CPLE_AppDefined,
×
1806
                     "Error : band %d has no color table",
1807
                     std::abs(psOptions->anBandList[0]));
×
1808
            GDALClose(poVDS);
×
1809
            return nullptr;
×
1810
        }
1811

1812
        /* Check that the color table only contains gray levels */
1813
        /* when using -expand gray */
1814
        if (psOptions->nRGBExpand == 1)
22✔
1815
        {
1816
            int nColorCount = poColorTable->GetColorEntryCount();
1✔
1817
            for (int nColor = 0; nColor < nColorCount; nColor++)
3✔
1818
            {
1819
                const GDALColorEntry *poEntry =
1820
                    poColorTable->GetColorEntry(nColor);
2✔
1821
                if (poEntry->c1 != poEntry->c2 || poEntry->c1 != poEntry->c3)
2✔
1822
                {
1823
                    CPLError(CE_Warning, CPLE_AppDefined,
×
1824
                             "Warning : color table contains non gray levels "
1825
                             "colors");
1826
                    break;
×
1827
                }
1828
            }
1829
        }
1830

1831
        if (psOptions->nBandCount == 1)
22✔
1832
        {
1833
            psOptions->nBandCount = psOptions->nRGBExpand;
21✔
1834
        }
1835
        else if (psOptions->nBandCount == 2 &&
2✔
1836
                 (psOptions->nRGBExpand == 3 || psOptions->nRGBExpand == 4))
1✔
1837
        {
1838
            psOptions->nBandCount = psOptions->nRGBExpand;
1✔
1839
        }
1840
        else
1841
        {
1842
            CPLError(CE_Failure, CPLE_IllegalArg,
×
1843
                     "Error : invalid use of -expand option.");
1844
            GDALClose(poVDS);
×
1845
            return nullptr;
×
1846
        }
1847
    }
1848

1849
    // Can be set to TRUE in the band loop too
1850
    bool bFilterOutStatsMetadata =
1851
        !psOptions->asScaleParams.empty() || psOptions->bUnscale ||
3,513✔
1852
        !bSpatialArrangementPreserved || psOptions->nRGBExpand != 0;
3,513✔
1853

1854
    if (static_cast<int>(psOptions->anColorInterp.size()) >
1,789✔
1855
        psOptions->nBandCount)
1,789✔
1856
    {
1857
        CPLError(CE_Warning, CPLE_AppDefined,
1✔
1858
                 "More bands defined in -colorinterp than output bands");
1859
    }
1860

1861
    /* ==================================================================== */
1862
    /*      Process all bands.                                              */
1863
    /* ==================================================================== */
1864
    GDALDataType eOutputType = psOptions->eOutputType;
1,789✔
1865

1866
    for (int i = 0; i < psOptions->nBandCount; i++)
6,101✔
1867
    {
1868
        int nComponent = 0;
4,313✔
1869
        int nSrcBand = 0;
4,313✔
1870

1871
        if (psOptions->nRGBExpand != 0)
4,313✔
1872
        {
1873
            if (nSrcBandCount == 2 && psOptions->nRGBExpand == 4 && i == 3)
70✔
1874
                nSrcBand = psOptions->anBandList[1];
1✔
1875
            else
1876
            {
1877
                nSrcBand = psOptions->anBandList[0];
69✔
1878
                nComponent = i + 1;
69✔
1879
            }
1880
        }
1881
        else
1882
        {
1883
            nSrcBand = psOptions->anBandList[i];
4,243✔
1884
        }
1885

1886
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(std::abs(nSrcBand));
4,313✔
1887

1888
        /* --------------------------------------------------------------------
1889
         */
1890
        /*      Select output data type to match source. */
1891
        /* --------------------------------------------------------------------
1892
         */
1893
        GDALRasterBand *poRealSrcBand =
1894
            (nSrcBand < 0) ? poSrcBand->GetMaskBand() : poSrcBand;
4,313✔
1895
        GDALDataType eBandType;
1896
        if (eOutputType == GDT_Unknown)
4,313✔
1897
        {
1898
            eBandType = poRealSrcBand->GetRasterDataType();
3,775✔
1899
            if (eBandType != GDT_Byte && psOptions->nRGBExpand != 0)
3,775✔
1900
            {
1901
                // Use case of https://github.com/OSGeo/gdal/issues/9402
1902
                if (const auto poColorTable = poRealSrcBand->GetColorTable())
5✔
1903
                {
1904
                    bool bIn0To255Range = true;
5✔
1905
                    const int nColorCount = poColorTable->GetColorEntryCount();
5✔
1906
                    for (int nColor = 0; nColor < nColorCount; nColor++)
6✔
1907
                    {
1908
                        const GDALColorEntry *poEntry =
1909
                            poColorTable->GetColorEntry(nColor);
5✔
1910
                        if (poEntry->c1 > 255 || poEntry->c2 > 255 ||
5✔
1911
                            poEntry->c3 > 255 || poEntry->c4 > 255)
3✔
1912
                        {
1913
                            bIn0To255Range = false;
4✔
1914
                            break;
4✔
1915
                        }
1916
                    }
1917
                    if (bIn0To255Range)
5✔
1918
                    {
1919
                        if (!psOptions->bQuiet)
1✔
1920
                        {
1921
                            CPLError(CE_Warning, CPLE_AppDefined,
1✔
1922
                                     "Using Byte output data type due to range "
1923
                                     "of values in color table");
1924
                        }
1925
                        eBandType = GDT_Byte;
1✔
1926
                    }
1927
                }
1928
                eOutputType = eBandType;
5✔
1929
            }
1930
        }
1931
        else
1932
        {
1933
            eBandType = eOutputType;
538✔
1934

1935
            // Check that we can copy existing statistics
1936
            GDALDataType eSrcBandType = poRealSrcBand->GetRasterDataType();
538✔
1937
            const char *pszMin =
1938
                poRealSrcBand->GetMetadataItem("STATISTICS_MINIMUM");
538✔
1939
            const char *pszMax =
1940
                poRealSrcBand->GetMetadataItem("STATISTICS_MAXIMUM");
538✔
1941
            if (!bFilterOutStatsMetadata && eBandType != eSrcBandType &&
538✔
1942
                pszMin != nullptr && pszMax != nullptr)
4✔
1943
            {
1944
                const bool bSrcIsInteger =
1945
                    CPL_TO_BOOL(GDALDataTypeIsInteger(eSrcBandType) &&
8✔
1946
                                !GDALDataTypeIsComplex(eSrcBandType));
4✔
1947
                const bool bDstIsInteger =
1948
                    CPL_TO_BOOL(GDALDataTypeIsInteger(eBandType) &&
7✔
1949
                                !GDALDataTypeIsComplex(eBandType));
3✔
1950
                if (bSrcIsInteger && bDstIsInteger)
4✔
1951
                {
1952
                    std::int64_t nDstMin = 0;
3✔
1953
                    std::uint64_t nDstMax = 0;
3✔
1954
                    switch (eBandType)
3✔
1955
                    {
1956
                        case GDT_Byte:
1✔
1957
                            nDstMin = std::numeric_limits<std::uint8_t>::min();
1✔
1958
                            nDstMax = std::numeric_limits<std::uint8_t>::max();
1✔
1959
                            break;
1✔
1960
                        case GDT_Int8:
×
1961
                            nDstMin = std::numeric_limits<std::int8_t>::min();
×
1962
                            nDstMax = std::numeric_limits<std::int8_t>::max();
×
1963
                            break;
×
1964
                        case GDT_UInt16:
2✔
1965
                            nDstMin = std::numeric_limits<std::uint16_t>::min();
2✔
1966
                            nDstMax = std::numeric_limits<std::uint16_t>::max();
2✔
1967
                            break;
2✔
1968
                        case GDT_Int16:
×
1969
                            nDstMin = std::numeric_limits<std::int16_t>::min();
×
1970
                            nDstMax = std::numeric_limits<std::int16_t>::max();
×
1971
                            break;
×
1972
                        case GDT_UInt32:
×
1973
                            nDstMin = std::numeric_limits<std::uint32_t>::min();
×
1974
                            nDstMax = std::numeric_limits<std::uint32_t>::max();
×
1975
                            break;
×
1976
                        case GDT_Int32:
×
1977
                            nDstMin = std::numeric_limits<std::int32_t>::min();
×
1978
                            nDstMax = std::numeric_limits<std::int32_t>::max();
×
1979
                            break;
×
1980
                        case GDT_UInt64:
×
1981
                            nDstMin = std::numeric_limits<std::uint64_t>::min();
×
1982
                            nDstMax = std::numeric_limits<std::uint64_t>::max();
×
1983
                            break;
×
1984
                        case GDT_Int64:
×
1985
                            nDstMin = std::numeric_limits<std::int64_t>::min();
×
1986
                            nDstMax = std::numeric_limits<std::int64_t>::max();
×
1987
                            break;
×
1988
                        default:
×
1989
                            CPLAssert(false);
×
1990
                            break;
1991
                    }
1992

1993
                    try
1994
                    {
1995
                        const auto nMin = std::stoll(pszMin);
3✔
1996
                        const auto nMax = std::stoull(pszMax);
3✔
1997
                        if (nMin < nDstMin || nMax > nDstMax)
3✔
1998
                            bFilterOutStatsMetadata = true;
1✔
1999
                    }
2000
                    catch (const std::exception &)
×
2001
                    {
2002
                    }
3✔
2003
                }
2004
                // Float64 is large enough to hold all integer <= 32 bit or
2005
                // float32 values there might be other OK cases, but ere on safe
2006
                // side for now
2007
                else if (!((bSrcIsInteger || eSrcBandType == GDT_Float32) &&
1✔
2008
                           eBandType == GDT_Float64))
2009
                {
2010
                    bFilterOutStatsMetadata = true;
×
2011
                }
2012
            }
2013
        }
2014

2015
        /* --------------------------------------------------------------------
2016
         */
2017
        /*      Create this band. */
2018
        /* --------------------------------------------------------------------
2019
         */
2020
        CPLStringList aosAddBandOptions;
4,313✔
2021
        int nSrcBlockXSize, nSrcBlockYSize;
2022
        poSrcBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
4,313✔
2023
        if (bKeepResolution &&
3,890✔
2024
            (fmod(psOptions->srcWin.dfXOff, nSrcBlockXSize)) == 0 &&
8,203✔
2025
            (fmod(psOptions->srcWin.dfYOff, nSrcBlockYSize)) == 0)
1,424✔
2026
        {
2027
            aosAddBandOptions.SetNameValue("BLOCKXSIZE",
2028
                                           CPLSPrintf("%d", nSrcBlockXSize));
1,203✔
2029
            aosAddBandOptions.SetNameValue("BLOCKYSIZE",
2030
                                           CPLSPrintf("%d", nSrcBlockYSize));
1,203✔
2031
        }
2032
        const char *pszBlockXSize =
2033
            psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE");
4,313✔
2034
        if (pszBlockXSize)
4,313✔
2035
            aosAddBandOptions.SetNameValue("BLOCKXSIZE", pszBlockXSize);
55✔
2036
        const char *pszBlockYSize =
2037
            psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE");
4,313✔
2038
        if (pszBlockYSize)
4,313✔
2039
            aosAddBandOptions.SetNameValue("BLOCKYSIZE", pszBlockYSize);
70✔
2040
        poVDS->AddBand(eBandType, aosAddBandOptions.List());
4,313✔
2041
        VRTSourcedRasterBand *poVRTBand =
2042
            static_cast<VRTSourcedRasterBand *>(poVDS->GetRasterBand(i + 1));
4,313✔
2043

2044
        if (nSrcBand < 0)
4,313✔
2045
        {
2046
            poVRTBand->AddMaskBandSource(
14✔
2047
                poSrcBand, psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
14✔
2048
                psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize,
14✔
2049
                dstWin.dfXOff, dstWin.dfYOff, dstWin.dfXSize, dstWin.dfYSize);
2050

2051
            // Color interpretation override
2052
            if (!psOptions->anColorInterp.empty())
14✔
2053
            {
2054
                if (i < static_cast<int>(psOptions->anColorInterp.size()) &&
18✔
2055
                    psOptions->anColorInterp[i] >= 0)
9✔
2056
                {
2057
                    poVRTBand->SetColorInterpretation(
9✔
2058
                        static_cast<GDALColorInterp>(
2059
                            psOptions->anColorInterp[i]));
9✔
2060
                }
2061
            }
2062

2063
            continue;
14✔
2064
        }
2065

2066
        // Preserve NBITS if no option change values
2067
        const char *pszNBits =
2068
            poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
4,299✔
2069
        if (pszNBits && psOptions->nRGBExpand == 0 &&
27✔
2070
            psOptions->asScaleParams.empty() && !psOptions->bUnscale &&
26✔
2071
            psOptions->eOutputType == GDT_Unknown &&
4,326✔
2072
            psOptions->osResampling.empty())
13✔
2073
        {
2074
            poVRTBand->SetMetadataItem("NBITS", pszNBits, "IMAGE_STRUCTURE");
1✔
2075
        }
2076

2077
        // Preserve PIXELTYPE if no option change values
2078
        if (poSrcBand->GetRasterDataType() == GDT_Byte &&
4,299✔
2079
            psOptions->nRGBExpand == 0 && psOptions->asScaleParams.empty() &&
4,111✔
2080
            !psOptions->bUnscale && psOptions->eOutputType == GDT_Unknown &&
11,944✔
2081
            psOptions->osResampling.empty())
3,534✔
2082
        {
2083
            poSrcBand->EnablePixelTypeSignedByteWarning(false);
3,369✔
2084
            const char *pszPixelType =
2085
                poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
3,369✔
2086
            poSrcBand->EnablePixelTypeSignedByteWarning(true);
3,369✔
2087
            if (pszPixelType)
3,369✔
2088
            {
2089
                poVRTBand->SetMetadataItem("PIXELTYPE", pszPixelType,
1✔
2090
                                           "IMAGE_STRUCTURE");
1✔
2091
            }
2092
        }
2093

2094
        const char *pszCompression =
2095
            poSrcBand->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
4,299✔
2096
        if (pszCompression)
4,299✔
2097
        {
2098
            poVRTBand->SetMetadataItem("COMPRESSION", pszCompression,
9✔
2099
                                       "IMAGE_STRUCTURE");
9✔
2100
        }
2101

2102
        /* --------------------------------------------------------------------
2103
         */
2104
        /*      Do we need to collect scaling information? */
2105
        /* --------------------------------------------------------------------
2106
         */
2107
        double dfScale = 1.0;
4,299✔
2108
        double dfOffset = 0.0;
4,299✔
2109
        bool bScale = false;
4,299✔
2110
        double dfScaleSrcMin = std::numeric_limits<double>::quiet_NaN();
4,299✔
2111
        double dfScaleSrcMax = std::numeric_limits<double>::quiet_NaN();
4,299✔
2112
        double dfScaleDstMin = std::numeric_limits<double>::quiet_NaN();
4,299✔
2113
        double dfScaleDstMax = std::numeric_limits<double>::quiet_NaN();
4,299✔
2114
        bool bExponentScaling = false;
4,299✔
2115
        double dfExponent = 0.0;
4,299✔
2116

2117
        if (i < static_cast<int>(psOptions->asScaleParams.size()) &&
4,377✔
2118
            psOptions->asScaleParams[i].bScale)
78✔
2119
        {
2120
            bScale = psOptions->asScaleParams[i].bScale;
67✔
2121
            dfScaleSrcMin = psOptions->asScaleParams[i].dfScaleSrcMin;
67✔
2122
            dfScaleSrcMax = psOptions->asScaleParams[i].dfScaleSrcMax;
67✔
2123
            dfScaleDstMin = psOptions->asScaleParams[i].dfScaleDstMin;
67✔
2124
            dfScaleDstMax = psOptions->asScaleParams[i].dfScaleDstMax;
67✔
2125
        }
2126
        else if (psOptions->asScaleParams.size() == 1 &&
4,265✔
2127
                 !psOptions->bHasUsedExplicitScaleBand)
33✔
2128
        {
2129
            bScale = psOptions->asScaleParams[0].bScale;
32✔
2130
            dfScaleSrcMin = psOptions->asScaleParams[0].dfScaleSrcMin;
32✔
2131
            dfScaleSrcMax = psOptions->asScaleParams[0].dfScaleSrcMax;
32✔
2132
            dfScaleDstMin = psOptions->asScaleParams[0].dfScaleDstMin;
32✔
2133
            dfScaleDstMax = psOptions->asScaleParams[0].dfScaleDstMax;
32✔
2134
        }
2135

2136
        if (i < static_cast<int>(psOptions->adfExponent.size()) &&
4,326✔
2137
            psOptions->adfExponent[i] != 0.0)
27✔
2138
        {
2139
            bExponentScaling = TRUE;
24✔
2140
            dfExponent = psOptions->adfExponent[i];
24✔
2141
        }
2142
        else if (psOptions->adfExponent.size() == 1 &&
4,279✔
2143
                 !psOptions->bHasUsedExplicitExponentBand)
4✔
2144
        {
2145
            bExponentScaling = TRUE;
3✔
2146
            dfExponent = psOptions->adfExponent[0];
3✔
2147
        }
2148

2149
        if (bExponentScaling && !bScale)
4,299✔
2150
        {
2151
            CPLError(CE_Failure, CPLE_IllegalArg,
1✔
2152
                     "For band %d, -scale should be specified when -exponent "
2153
                     "is specified.",
2154
                     i + 1);
2155
            if (pbUsageError)
1✔
2156
                *pbUsageError = TRUE;
×
2157
            delete poVDS;
1✔
2158
            poSrcDS->Release();
1✔
2159
            return nullptr;
1✔
2160
        }
2161

2162
        if (bScale && std::isnan(dfScaleSrcMin))
4,298✔
2163
        {
2164
            double adfCMinMax[2] = {};
13✔
2165
            GDALComputeRasterMinMax(poSrcBand, TRUE, adfCMinMax);
13✔
2166
            dfScaleSrcMin = adfCMinMax[0];
13✔
2167
            dfScaleSrcMax = adfCMinMax[1];
13✔
2168
        }
2169

2170
        if (bScale)
4,298✔
2171
        {
2172
            /* To avoid a divide by zero */
2173
            if (dfScaleSrcMax == dfScaleSrcMin)
99✔
2174
                dfScaleSrcMax += 0.1;
×
2175

2176
            // Can still occur for very big values
2177
            if (dfScaleSrcMax == dfScaleSrcMin)
99✔
2178
            {
2179
                CPLError(CE_Failure, CPLE_AppDefined,
×
2180
                         "-scale cannot be applied due to source "
2181
                         "minimum and maximum being equal");
2182
                delete poVDS;
×
2183
                poSrcDS->Release();
×
2184
                return nullptr;
×
2185
            }
2186

2187
            if (std::isnan(dfScaleDstMin))
99✔
2188
            {
2189
                switch (poVRTBand->GetRasterDataType())
16✔
2190
                {
2191
                    case GDT_Byte:
5✔
2192
                        dfScaleDstMin = std::numeric_limits<uint8_t>::lowest();
5✔
2193
                        dfScaleDstMax = std::numeric_limits<uint8_t>::max();
5✔
2194
                        break;
5✔
2195
                    case GDT_Int8:
1✔
2196
                        dfScaleDstMin = std::numeric_limits<int8_t>::lowest();
1✔
2197
                        dfScaleDstMax = std::numeric_limits<int8_t>::max();
1✔
2198
                        break;
1✔
2199
                    case GDT_UInt16:
1✔
2200
                        dfScaleDstMin = std::numeric_limits<uint16_t>::lowest();
1✔
2201
                        dfScaleDstMax = std::numeric_limits<uint16_t>::max();
1✔
2202
                        break;
1✔
2203
                    case GDT_Int16:
1✔
2204
                    case GDT_CInt16:
2205
                        dfScaleDstMin = std::numeric_limits<int16_t>::lowest();
1✔
2206
                        dfScaleDstMax = std::numeric_limits<int16_t>::max();
1✔
2207
                        break;
1✔
2208
                    case GDT_UInt32:
1✔
2209
                        dfScaleDstMin = std::numeric_limits<uint32_t>::lowest();
1✔
2210
                        dfScaleDstMax = std::numeric_limits<uint32_t>::max();
1✔
2211
                        break;
1✔
2212
                    case GDT_Int32:
1✔
2213
                    case GDT_CInt32:
2214
                        dfScaleDstMin = std::numeric_limits<int32_t>::lowest();
1✔
2215
                        dfScaleDstMax = std::numeric_limits<int32_t>::max();
1✔
2216
                        break;
1✔
2217
                    case GDT_UInt64:
1✔
2218
                        dfScaleDstMin = static_cast<double>(
1✔
2219
                            std::numeric_limits<uint64_t>::lowest());
1✔
2220
                        dfScaleDstMax = static_cast<double>(
1✔
2221
                            std::numeric_limits<uint64_t>::max() - 2048);
1✔
2222
                        break;
1✔
2223
                    case GDT_Int64:
1✔
2224
                        dfScaleDstMin = static_cast<double>(
1✔
2225
                            std::numeric_limits<int64_t>::lowest() + 1024);
1✔
2226
                        dfScaleDstMax = static_cast<double>(
1✔
2227
                            std::numeric_limits<int64_t>::max() - 2048);
1✔
2228
                        break;
1✔
2229
                    case GDT_Float16:
4✔
2230
                    case GDT_Float32:
2231
                    case GDT_Float64:
2232
                    case GDT_CFloat16:
2233
                    case GDT_CFloat32:
2234
                    case GDT_CFloat64:
2235
                    case GDT_Unknown:
2236
                    case GDT_TypeCount:
2237
                        dfScaleDstMin = 0;
4✔
2238
                        dfScaleDstMax = 1;
4✔
2239
                        break;
4✔
2240
                }
2241
            }
2242

2243
            if (!bExponentScaling)
99✔
2244
            {
2245
                dfScale = (dfScaleDstMax - dfScaleDstMin) /
73✔
2246
                          (dfScaleSrcMax - dfScaleSrcMin);
73✔
2247
                dfOffset = -1 * dfScaleSrcMin * dfScale + dfScaleDstMin;
73✔
2248
            }
2249
        }
2250

2251
        if (psOptions->bUnscale)
4,298✔
2252
        {
2253
            dfScale = poSrcBand->GetScale();
25✔
2254
            dfOffset = poSrcBand->GetOffset();
25✔
2255
        }
2256

2257
        /* --------------------------------------------------------------------
2258
         */
2259
        /*      Create a simple or complex data source depending on the */
2260
        /*      translation type required. */
2261
        /* --------------------------------------------------------------------
2262
         */
2263
        VRTSimpleSource *poSimpleSource = nullptr;
4,298✔
2264
        if (psOptions->bUnscale || bScale ||
8,472✔
2265
            (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand))
4,174✔
2266
        {
2267
            VRTComplexSource *poSource = new VRTComplexSource();
194✔
2268

2269
            /* --------------------------------------------------------------------
2270
             */
2271
            /*      Set complex parameters. */
2272
            /* --------------------------------------------------------------------
2273
             */
2274

2275
            if (dfOffset != 0.0 || dfScale != 1.0)
194✔
2276
            {
2277
                poSource->SetLinearScaling(dfOffset, dfScale);
89✔
2278
            }
2279
            else if (bExponentScaling)
105✔
2280
            {
2281
                poSource->SetPowerScaling(dfExponent, dfScaleSrcMin,
26✔
2282
                                          dfScaleSrcMax, dfScaleDstMin,
2283
                                          dfScaleDstMax, !psOptions->bNoClip);
26✔
2284
            }
2285

2286
            poSource->SetColorTableComponent(nComponent);
194✔
2287

2288
            int bSuccess;
2289
            double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
194✔
2290
            if (bSuccess)
194✔
2291
            {
2292
                poSource->SetNoDataValue(dfNoData);
16✔
2293
            }
2294

2295
            poSimpleSource = poSource;
194✔
2296
        }
2297
        else
2298
        {
2299
            poSimpleSource = new VRTSimpleSource();
4,104✔
2300
        }
2301

2302
        poSimpleSource->SetResampling(psOptions->osResampling.empty()
4,478✔
2303
                                          ? nullptr
2304
                                          : psOptions->osResampling.c_str());
180✔
2305
        poVRTBand->ConfigureSource(
4,298✔
2306
            poSimpleSource, poSrcBand, FALSE, psOptions->srcWin.dfXOff,
4,298✔
2307
            psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
4,298✔
2308
            psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
4,298✔
2309
            dstWin.dfXSize, dstWin.dfYSize);
2310

2311
        poVRTBand->AddSource(poSimpleSource);
4,298✔
2312

2313
        /* --------------------------------------------------------------------
2314
         */
2315
        /*      In case of color table translate, we only set the color */
2316
        /*      interpretation other info copied by CopyBandInfo are */
2317
        /*      not relevant in RGB expansion. */
2318
        /* --------------------------------------------------------------------
2319
         */
2320
        if (psOptions->nRGBExpand == 1)
4,298✔
2321
        {
2322
            poVRTBand->SetColorInterpretation(GCI_GrayIndex);
1✔
2323
        }
2324
        else if (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand)
4,297✔
2325
        {
2326
            poVRTBand->SetColorInterpretation(
69✔
2327
                static_cast<GDALColorInterp>(GCI_RedBand + i));
69✔
2328
        }
2329

2330
        /* --------------------------------------------------------------------
2331
         */
2332
        /*      copy over some other information of interest. */
2333
        /* --------------------------------------------------------------------
2334
         */
2335
        else
2336
        {
2337
            CopyBandInfo(poSrcBand, poVRTBand,
4,228✔
2338
                         !psOptions->bStats && !bFilterOutStatsMetadata,
4,228✔
2339
                         !psOptions->bUnscale && !psOptions->bSetScale &&
8,428✔
2340
                             !psOptions->bSetOffset,
4,200✔
2341
                         !psOptions->bSetNoData && !psOptions->bUnsetNoData,
4,228✔
2342
                         !psOptions->bNoRAT, psOptions.get());
4,228✔
2343
            if (psOptions->asScaleParams.empty() &&
8,343✔
2344
                psOptions->adfExponent.empty() &&
8,343✔
2345
                EQUAL(psOptions->osFormat.c_str(), "GRIB"))
4,115✔
2346
            {
2347
                char **papszMD_GRIB = poSrcBand->GetMetadata("GRIB");
×
2348
                if (papszMD_GRIB != nullptr)
×
2349
                    poVRTBand->SetMetadata(papszMD_GRIB, "GRIB");
×
2350
            }
2351
        }
2352

2353
        // Color interpretation override
2354
        if (!psOptions->anColorInterp.empty())
4,298✔
2355
        {
2356
            if (i < static_cast<int>(psOptions->anColorInterp.size()) &&
90✔
2357
                psOptions->anColorInterp[i] >= 0)
44✔
2358
            {
2359
                poVRTBand->SetColorInterpretation(
19✔
2360
                    static_cast<GDALColorInterp>(psOptions->anColorInterp[i]));
19✔
2361
            }
2362
        }
2363

2364
        /* --------------------------------------------------------------------
2365
         */
2366
        /*      Set a forcible nodata value? */
2367
        /* --------------------------------------------------------------------
2368
         */
2369
        if (psOptions->bSetNoData)
4,298✔
2370
        {
2371
            const char *pszPixelType =
2372
                psOptions->aosCreateOptions.FetchNameValue("PIXELTYPE");
80✔
2373
            if (pszPixelType == nullptr &&
159✔
2374
                poVRTBand->GetRasterDataType() == GDT_Byte)
79✔
2375
            {
2376
                poVRTBand->EnablePixelTypeSignedByteWarning(false);
29✔
2377
                pszPixelType =
2378
                    poVRTBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
29✔
2379
                poVRTBand->EnablePixelTypeSignedByteWarning(true);
29✔
2380
            }
2381

2382
            bool bCannotBeExactlyRepresented = false;
80✔
2383

2384
            if (pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE"))
80✔
2385
            {
2386
                char *endptr = nullptr;
2✔
2387
                const double dfVal =
2388
                    CPLStrtod(psOptions->osNoData.c_str(), &endptr);
2✔
2389
                if (endptr == psOptions->osNoData.c_str() +
2✔
2390
                                  psOptions->osNoData.size() &&
4✔
2391
                    dfVal >= -128.0 && dfVal <= 127.0 &&
6✔
2392
                    static_cast<int8_t>(dfVal) == dfVal)
2✔
2393
                {
2394
                    poVRTBand->SetNoDataValue(dfVal);
2✔
2395
                }
2396
                else
2397
                {
2398
                    bCannotBeExactlyRepresented = true;
×
2399
                }
2✔
2400
            }
2401
            else
2402
            {
2403
                poVRTBand->SetNoDataValueAsString(psOptions->osNoData.c_str(),
78✔
2404
                                                  &bCannotBeExactlyRepresented);
2405
            }
2406
            if (bCannotBeExactlyRepresented)
80✔
2407
            {
2408
                CPLError(CE_Warning, CPLE_AppDefined,
4✔
2409
                         "Nodata value was not set to output band, "
2410
                         "as it cannot be represented on its data type.");
2411
            }
2412
        }
2413

2414
        if (psOptions->bSetScale)
4,298✔
2415
            poVRTBand->SetScale(psOptions->dfScale);
4✔
2416

2417
        if (psOptions->bSetOffset)
4,298✔
2418
            poVRTBand->SetOffset(psOptions->dfOffset);
5✔
2419

2420
        if (psOptions->eMaskMode == MASK_AUTO &&
4,298✔
2421
            (poSrcDS->GetRasterBand(1)->GetMaskFlags() & GMF_PER_DATASET) ==
4,196✔
2422
                0 &&
8,494✔
2423
            (poSrcBand->GetMaskFlags() & (GMF_ALL_VALID | GMF_NODATA)) == 0)
3,818✔
2424
        {
2425
            if (poVRTBand->CreateMaskBand(poSrcBand->GetMaskFlags()) == CE_None)
6✔
2426
            {
2427
                VRTSourcedRasterBand *hMaskVRTBand =
2428
                    cpl::down_cast<VRTSourcedRasterBand *>(
6✔
2429
                        poVRTBand->GetMaskBand());
6✔
2430
                hMaskVRTBand->AddMaskBandSource(
6✔
2431
                    poSrcBand, psOptions->srcWin.dfXOff,
6✔
2432
                    psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
6✔
2433
                    psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
6✔
2434
                    dstWin.dfXSize, dstWin.dfYSize);
2435
            }
2436
        }
2437
    }
2438

2439
    if (psOptions->eMaskMode == MASK_USER)
1,788✔
2440
    {
2441
        GDALRasterBand *poSrcBand =
2442
            poSrcDS->GetRasterBand(std::abs(psOptions->nMaskBand));
25✔
2443
        if (poSrcBand && poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
25✔
2444
        {
2445
            VRTSourcedRasterBand *hMaskVRTBand =
2446
                static_cast<VRTSourcedRasterBand *>(GDALGetMaskBand(
25✔
2447
                    GDALGetRasterBand(static_cast<GDALDataset *>(poVDS), 1)));
2448
            if (psOptions->nMaskBand > 0)
25✔
2449
                hMaskVRTBand->AddSimpleSource(
23✔
2450
                    poSrcBand, psOptions->srcWin.dfXOff,
23✔
2451
                    psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
23✔
2452
                    psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
23✔
2453
                    dstWin.dfXSize, dstWin.dfYSize);
2454
            else
2455
                hMaskVRTBand->AddMaskBandSource(
2✔
2456
                    poSrcBand, psOptions->srcWin.dfXOff,
2✔
2457
                    psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
2✔
2458
                    psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
2✔
2459
                    dstWin.dfXSize, dstWin.dfYSize);
2460
        }
2461
    }
2462
    else if (psOptions->eMaskMode == MASK_AUTO && nSrcBandCount > 0 &&
3,510✔
2463
             poSrcDS->GetRasterBand(1)->GetMaskFlags() == GMF_PER_DATASET)
1,747✔
2464
    {
2465
        if (poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
3✔
2466
        {
2467
            VRTSourcedRasterBand *hMaskVRTBand =
2468
                static_cast<VRTSourcedRasterBand *>(GDALGetMaskBand(
3✔
2469
                    GDALGetRasterBand(static_cast<GDALDataset *>(poVDS), 1)));
2470
            hMaskVRTBand->AddMaskBandSource(
3✔
2471
                poSrcDS->GetRasterBand(1), psOptions->srcWin.dfXOff,
3✔
2472
                psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
3✔
2473
                psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
3✔
2474
                dstWin.dfXSize, dstWin.dfYSize);
2475
        }
2476
    }
2477

2478
    /* -------------------------------------------------------------------- */
2479
    /*      Compute stats if required.                                      */
2480
    /* -------------------------------------------------------------------- */
2481
    if (psOptions->bStats && EQUAL(psOptions->osFormat.c_str(), "COG"))
1,788✔
2482
    {
2483
        psOptions->aosCreateOptions.SetNameValue("STATISTICS", "YES");
×
2484
    }
2485
    else if (psOptions->bStats)
1,788✔
2486
    {
2487
        for (int i = 0; i < poVDS->GetRasterCount(); i++)
2✔
2488
        {
2489
            double dfMin, dfMax, dfMean, dfStdDev;
2490
            poVDS->GetRasterBand(i + 1)->ComputeStatistics(
1✔
2491
                psOptions->bApproxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
1✔
2492
                GDALDummyProgress, nullptr);
1✔
2493
        }
2494
    }
2495

2496
    /* -------------------------------------------------------------------- */
2497
    /*      Write to the output file using CopyCreate().                    */
2498
    /* -------------------------------------------------------------------- */
2499
    if (EQUAL(psOptions->osFormat.c_str(), "VRT") &&
2,143✔
2500
        (psOptions->aosCreateOptions.empty() ||
355✔
2501
         (psOptions->aosCreateOptions.size() == 1 &&
16✔
2502
          psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE")) ||
4✔
2503
         (psOptions->aosCreateOptions.size() == 1 &&
16✔
2504
          psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE")) ||
4✔
2505
         (psOptions->aosCreateOptions.size() == 2 &&
12✔
2506
          psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE") &&
12✔
2507
          psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE"))))
12✔
2508
    {
2509
        poVDS->SetDescription(pszDest);
355✔
2510
        hOutDS = GDALDataset::ToHandle(poVDS);
355✔
2511
        if (!EQUAL(pszDest, ""))
355✔
2512
        {
2513
            hOutDS = GDALTranslateFlush(hOutDS);
28✔
2514
        }
2515
    }
2516
    else
2517
    {
2518
        hOutDS = GDALCreateCopy(
1,433✔
2519
            hDriver, pszDest, GDALDataset::ToHandle(poVDS), psOptions->bStrict,
1,433✔
2520
            psOptions->aosCreateOptions.List(), psOptions->pfnProgress,
1,433✔
2521
            psOptions->pProgressData);
1,433✔
2522
        hOutDS = GDALTranslateFlush(hOutDS);
1,433✔
2523

2524
        GDALClose(poVDS);
1,433✔
2525
    }
2526

2527
    poSrcDS->Release();
1,788✔
2528

2529
    return hOutDS;
1,788✔
2530
}
2531

2532
/************************************************************************/
2533
/*                           AttachMetadata()                           */
2534
/************************************************************************/
2535

2536
static void AttachMetadata(GDALDatasetH hDS,
1,789✔
2537
                           const CPLStringList &aosMetadataOptions)
2538

2539
{
2540
    for (const auto &[pszKey, pszValue] :
62✔
2541
         cpl::IterateNameValue(aosMetadataOptions))
1,851✔
2542
    {
2543
        GDALSetMetadataItem(hDS, pszKey, pszValue, nullptr);
31✔
2544
    }
2545
}
1,789✔
2546

2547
/************************************************************************/
2548
/*                           AttachDomainMetadata()                     */
2549
/************************************************************************/
2550

2551
static void AttachDomainMetadata(GDALDatasetH hDS,
1,789✔
2552
                                 const CPLStringList &aosDomainMetadataOptions)
2553

2554
{
2555
    for (const char *pszStr : aosDomainMetadataOptions)
1,798✔
2556
    {
2557

2558
        char *pszKey = nullptr;
9✔
2559
        char *pszDomain = nullptr;
9✔
2560

2561
        // parse the DOMAIN:KEY=value, Remainder is KEY=value
2562
        const char *pszRemainder =
2563
            CPLParseNameValueSep(pszStr, &pszDomain, ':');
9✔
2564

2565
        if (pszDomain && pszRemainder)
9✔
2566
        {
2567

2568
            const char *pszValue =
2569
                CPLParseNameValueSep(pszRemainder, &pszKey, '=');
8✔
2570
            if (pszKey && pszValue)
8✔
2571
            {
2572
                GDALSetMetadataItem(hDS, pszKey, pszValue, pszDomain);
7✔
2573
            }
2574
        }
2575
        CPLFree(pszKey);
9✔
2576

2577
        CPLFree(pszDomain);
9✔
2578
    }
2579
}
1,789✔
2580

2581
/************************************************************************/
2582
/*                           CopyBandInfo()                            */
2583
/************************************************************************/
2584

2585
/* A bit of a clone of VRTRasterBand::CopyCommonInfoFrom(), but we need */
2586
/* more and more custom behavior in the context of gdal_translate ... */
2587

2588
static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
4,228✔
2589
                         int bCanCopyStatsMetadata, int bCopyScale,
2590
                         int bCopyNoData, bool bCopyRAT,
2591
                         const GDALTranslateOptions * /*psOptions*/)
2592

2593
{
2594

2595
    if (bCanCopyStatsMetadata)
4,228✔
2596
    {
2597
        poDstBand->SetMetadata(poSrcBand->GetMetadata());
794✔
2598
        if (bCopyRAT)
794✔
2599
        {
2600
            poDstBand->SetDefaultRAT(poSrcBand->GetDefaultRAT());
793✔
2601
        }
2602
    }
2603
    else
2604
    {
2605
        char **papszMetadata = poSrcBand->GetMetadata();
3,434✔
2606
        char **papszMetadataNew = nullptr;
3,434✔
2607
        for (int i = 0; papszMetadata != nullptr && papszMetadata[i] != nullptr;
3,523✔
2608
             i++)
2609
        {
2610
            if (!STARTS_WITH(papszMetadata[i], "STATISTICS_"))
89✔
2611
                papszMetadataNew =
2612
                    CSLAddString(papszMetadataNew, papszMetadata[i]);
71✔
2613
        }
2614
        poDstBand->SetMetadata(papszMetadataNew);
3,434✔
2615
        CSLDestroy(papszMetadataNew);
3,434✔
2616

2617
        // we need to strip histogram data from the source RAT
2618
        if (poSrcBand->GetDefaultRAT() && bCopyRAT)
3,434✔
2619
        {
2620
            GDALRasterAttributeTable *poNewRAT =
2621
                poSrcBand->GetDefaultRAT()->Clone();
2✔
2622

2623
            // strip histogram data (as defined by the source RAT)
2624
            poNewRAT->RemoveStatistics();
2✔
2625
            if (poNewRAT->GetColumnCount())
2✔
2626
            {
2627
                poDstBand->SetDefaultRAT(poNewRAT);
1✔
2628
            }
2629
            // since SetDefaultRAT copies the RAT data we need to delete our
2630
            // original
2631
            delete poNewRAT;
2✔
2632
        }
2633
    }
2634

2635
    poDstBand->SetColorTable(poSrcBand->GetColorTable());
4,228✔
2636
    poDstBand->SetColorInterpretation(poSrcBand->GetColorInterpretation());
4,228✔
2637
    if (strlen(poSrcBand->GetDescription()) > 0)
4,228✔
2638
        poDstBand->SetDescription(poSrcBand->GetDescription());
5✔
2639

2640
    if (bCopyNoData)
4,228✔
2641
    {
2642
        int bSuccess = FALSE;
4,142✔
2643
        CPL_IGNORE_RET_VAL(poSrcBand->GetNoDataValue(&bSuccess));
4,142✔
2644
        if (bSuccess)
4,142✔
2645
        {
2646
            bool bCannotBeExactlyRepresented = false;
99✔
2647
            if (!GDALCopyNoDataValue(poDstBand, poSrcBand,
99✔
2648
                                     &bCannotBeExactlyRepresented) &&
99✔
2649
                bCannotBeExactlyRepresented)
2650
            {
2651
                CPLError(CE_Warning, CPLE_AppDefined,
×
2652
                         "Source nodata value was not copied to output band, "
2653
                         "as it cannot be represented on its data type.");
2654
            }
2655
        }
2656
    }
2657

2658
    if (bCopyScale)
4,228✔
2659
    {
2660
        poDstBand->SetOffset(poSrcBand->GetOffset());
4,197✔
2661
        poDstBand->SetScale(poSrcBand->GetScale());
4,197✔
2662
    }
2663

2664
    poDstBand->SetCategoryNames(poSrcBand->GetCategoryNames());
4,228✔
2665

2666
    // Copy unit only if the range of pixel values is not modified
2667
    if (bCanCopyStatsMetadata && bCopyScale &&
5,016✔
2668
        !EQUAL(poSrcBand->GetUnitType(), ""))
788✔
2669
        poDstBand->SetUnitType(poSrcBand->GetUnitType());
24✔
2670
}
4,228✔
2671

2672
/************************************************************************/
2673
/*                             GetColorInterp()                         */
2674
/************************************************************************/
2675

2676
static int GetColorInterp(const char *pszStr)
29✔
2677
{
2678
    if (EQUAL(pszStr, "undefined"))
29✔
2679
        return GCI_Undefined;
5✔
2680
    const int eInterp = GDALGetColorInterpretationByName(pszStr);
24✔
2681
    if (eInterp != GCI_Undefined)
24✔
2682
        return eInterp;
23✔
2683
    CPLError(CE_Warning, CPLE_NotSupported,
1✔
2684
             "Unsupported color interpretation: %s", pszStr);
2685
    return -1;
1✔
2686
}
2687

2688
/************************************************************************/
2689
/*                     GDALTranslateOptionsGetParser()                  */
2690
/************************************************************************/
2691

2692
static std::unique_ptr<GDALArgumentParser>
2693
GDALTranslateOptionsGetParser(GDALTranslateOptions *psOptions,
2,729✔
2694
                              GDALTranslateOptionsForBinary *psOptionsForBinary)
2695
{
2696
    auto argParser = std::make_unique<GDALArgumentParser>(
2697
        "gdal_translate", /* bForBinary=*/psOptionsForBinary != nullptr);
2,729✔
2698

2699
    argParser->add_description(
2,729✔
2700
        _("Convert raster data between different formats, with potential "
2701
          "subsetting, resampling, and rescaling pixels in the process."));
2,729✔
2702

2703
    argParser->add_epilog(_("For more details, consult "
2,729✔
2704
                            "https://gdal.org/programs/gdal_translate.html"));
2,729✔
2705

2706
    argParser->add_output_type_argument(psOptions->eOutputType);
2,729✔
2707

2708
    argParser->add_argument("-if")
2,729✔
2709
        .append()
2,729✔
2710
        .metavar("<format>")
5,458✔
2711
        .action(
2712
            [psOptionsForBinary](const std::string &s)
6✔
2713
            {
2714
                if (psOptionsForBinary)
3✔
2715
                {
2716
                    if (GDALGetDriverByName(s.c_str()) == nullptr)
3✔
2717
                    {
2718
                        CPLError(CE_Warning, CPLE_AppDefined,
1✔
2719
                                 "%s is not a recognized driver", s.c_str());
2720
                    }
2721
                    psOptionsForBinary->aosAllowedInputDrivers.AddString(
2722
                        s.c_str());
3✔
2723
                }
2724
            })
2,729✔
2725
        .help(_("Format/driver name(s) to try when opening the input file."));
2,729✔
2726

2727
    argParser->add_output_format_argument(psOptions->osFormat);
2,729✔
2728

2729
    argParser->add_quiet_argument(&(psOptions->bQuiet));
2,729✔
2730

2731
    argParser->add_argument("-b")
2,729✔
2732
        .append()
2,729✔
2733
        .metavar("<band>")
5,458✔
2734
        .action(
2735
            [psOptions](const std::string &s)
1,246✔
2736
            {
2737
                const char *pszBand = s.c_str();
609✔
2738
                bool bMask = false;
609✔
2739
                if (EQUAL(pszBand, "mask"))
609✔
2740
                    pszBand = "mask,1";
14✔
2741
                if (STARTS_WITH_CI(pszBand, "mask,"))
609✔
2742
                {
2743
                    bMask = true;
14✔
2744
                    pszBand += 5;
14✔
2745
                    /* If we use the source mask band as a regular band */
2746
                    /* don't create a target mask band by default */
2747
                    if (!psOptions->bParsedMaskArgument)
14✔
2748
                        psOptions->eMaskMode = MASK_DISABLED;
14✔
2749
                }
2750
                const int nBand = atoi(pszBand);
609✔
2751
                if (nBand < 1)
609✔
2752
                {
2753
                    throw std::invalid_argument(CPLSPrintf(
2754
                        "Unrecognizable band number (%s).", s.c_str()));
×
2755
                }
2756

2757
                psOptions->nBandCount++;
609✔
2758
                psOptions->anBandList.emplace_back(nBand * (bMask ? -1 : 1));
609✔
2759
            })
3,338✔
2760
        .help(_("Select input band(s)"));
2,729✔
2761

2762
    argParser->add_argument("-mask")
2,729✔
2763
        .metavar("<mask>")
5,458✔
2764
        .action(
2765
            [psOptions](const std::string &s)
54✔
2766
            {
2767
                psOptions->bParsedMaskArgument = true;
26✔
2768
                const char *pszBand = s.c_str();
26✔
2769
                if (EQUAL(pszBand, "none"))
26✔
2770
                {
2771
                    psOptions->eMaskMode = MASK_DISABLED;
1✔
2772
                }
2773
                else if (EQUAL(pszBand, "auto"))
25✔
2774
                {
2775
                    psOptions->eMaskMode = MASK_AUTO;
×
2776
                }
2777
                else
2778
                {
2779
                    bool bMask = false;
25✔
2780

2781
                    if (EQUAL(pszBand, "mask"))
25✔
2782
                        pszBand = "mask,1";
1✔
2783
                    if (STARTS_WITH_CI(pszBand, "mask,"))
25✔
2784
                    {
2785
                        bMask = true;
2✔
2786
                        pszBand += 5;
2✔
2787
                    }
2788
                    const int nBand = atoi(pszBand);
25✔
2789
                    if (nBand < 1)
25✔
2790
                    {
2791
                        throw std::invalid_argument(CPLSPrintf(
2792
                            "Unrecognizable band number (%s).", s.c_str()));
×
2793
                    }
2794

2795
                    psOptions->eMaskMode = MASK_USER;
25✔
2796
                    psOptions->nMaskBand = nBand;
25✔
2797
                    if (bMask)
25✔
2798
                        psOptions->nMaskBand *= -1;
2✔
2799
                }
2800
            })
2,755✔
2801
        .help(_("Select an input band to create output dataset mask band"));
2,729✔
2802

2803
    argParser->add_argument("-expand")
2,729✔
2804
        .metavar("gray|rgb|rgba")
5,458✔
2805
        .action(
2806
            [psOptions](const std::string &s)
44✔
2807
            {
2808
                if (EQUAL(s.c_str(), "gray"))
22✔
2809
                    psOptions->nRGBExpand = 1;
1✔
2810
                else if (EQUAL(s.c_str(), "rgb"))
21✔
2811
                    psOptions->nRGBExpand = 3;
15✔
2812
                else if (EQUAL(s.c_str(), "rgba"))
6✔
2813
                    psOptions->nRGBExpand = 4;
6✔
2814
                else
2815
                {
2816
                    throw std::invalid_argument(CPLSPrintf(
2817
                        "Value %s unsupported. Only gray, rgb or rgba are "
2818
                        "supported.",
2819
                        s.c_str()));
×
2820
                }
2821
            })
2,751✔
2822
        .help(_("To expose a dataset with 1 band with a color table as a "
2823
                "dataset with 3 (RGB) or 4 (RGBA) bands."));
2,729✔
2824

2825
    {
2826
        auto &group = argParser->add_mutually_exclusive_group();
2,729✔
2827
        group.add_argument("-strict")
2,729✔
2828
            .store_into(psOptions->bStrict)
2,729✔
2829
            .help(_("Enable strict mode"));
2,729✔
2830

2831
        group.add_argument("-not_strict")
2,729✔
2832
            .flag()
2,729✔
2833
            .action([psOptions](const std::string &)
×
2834
                    { psOptions->bStrict = false; })
2,729✔
2835
            .help(_("Disable strict mode"));
2,729✔
2836
    }
2837

2838
    argParser->add_argument("-outsize")
2,729✔
2839
        .metavar("<xsize[%]|0> <ysize[%]|0>")
5,458✔
2840
        .nargs(2)
2,729✔
2841
        .help(_("Set the size of the output file."));
2,729✔
2842

2843
    argParser->add_argument("-tr")
2,729✔
2844
        .metavar("<xres> <yes>")
5,458✔
2845
        .nargs(2)
2,729✔
2846
        .scan<'g', double>()
2,729✔
2847
        .help(_("Set target resolution."));
2,729✔
2848

2849
    argParser->add_argument("-ovr")
2,729✔
2850
        .metavar("<level>|AUTO|AUTO-<n>|NONE")
5,458✔
2851
        .action(
2852
            [psOptions](const std::string &s)
31✔
2853
            {
2854
                const char *pszOvLevel = s.c_str();
16✔
2855
                if (EQUAL(pszOvLevel, "AUTO"))
16✔
2856
                    psOptions->nOvLevel = OVR_LEVEL_AUTO;
×
2857
                else if (STARTS_WITH_CI(pszOvLevel, "AUTO-"))
16✔
2858
                    psOptions->nOvLevel =
4✔
2859
                        OVR_LEVEL_AUTO - atoi(pszOvLevel + strlen("AUTO-"));
4✔
2860
                else if (EQUAL(pszOvLevel, "NONE"))
12✔
2861
                    psOptions->nOvLevel = OVR_LEVEL_NONE;
1✔
2862
                else if (CPLGetValueType(pszOvLevel) == CPL_VALUE_INTEGER)
11✔
2863
                    psOptions->nOvLevel = atoi(pszOvLevel);
10✔
2864
                else
2865
                {
2866
                    throw std::invalid_argument(CPLSPrintf(
2867
                        "Invalid value '%s' for -ovr option", pszOvLevel));
1✔
2868
                }
2869
            })
2,744✔
2870
        .help(_("Specify which overview level of source file must be used"));
2,729✔
2871

2872
    if (psOptionsForBinary)
2,729✔
2873
    {
2874
        argParser->add_argument("-sds")
143✔
2875
            .store_into(psOptionsForBinary->bCopySubDatasets)
143✔
2876
            .help(_("Copy subdatasets"));
143✔
2877
    }
2878

2879
    argParser->add_argument("-r")
2,729✔
2880
        .metavar("nearest,bilinear,cubic,cubicspline,lanczos,average,mode")
5,458✔
2881
        .store_into(psOptions->osResampling)
2,729✔
2882
        .help(_("Resampling algorithm."));
2,729✔
2883

2884
    {
2885
        auto &group = argParser->add_mutually_exclusive_group();
2,729✔
2886
        group.add_argument("-scale")
2,729✔
2887
            .metavar("[<src_min> <src_max> [<dst_min> <dst_max>]]")
5,458✔
2888
            //.nargs(0, 4)
2889
            .append()
2,729✔
2890
            .scan<'g', double>()
2,729✔
2891
            .help(_("Rescale the input pixels values from the range src_min to "
2892
                    "src_max to the range dst_min to dst_max."));
2,729✔
2893

2894
        group.add_argument("-scale_X")
2,729✔
2895
            .metavar("[<src_min> <src_max> [<dst_min> <dst_max>]]")
5,458✔
2896
            //.nargs(0, 4)
2897
            .append()
2,729✔
2898
            .scan<'g', double>()
2,729✔
2899
            .help(_("Rescale the input pixels values for band X."));
2,729✔
2900

2901
        group.add_argument("-unscale")
2,729✔
2902
            .store_into(psOptions->bUnscale)
2,729✔
2903
            .help(_("Apply the scale/offset metadata for the bands to convert "
2904
                    "scaled values to unscaled values."));
2,729✔
2905
    }
2906

2907
    {
2908
        auto &group = argParser->add_mutually_exclusive_group();
2,729✔
2909
        group.add_argument("-exponent")
2,729✔
2910
            .metavar("<value>")
5,458✔
2911
            .scan<'g', double>()
2,729✔
2912
            .help(_(
2913
                "Exponent to apply non-linear scaling with a power function"));
2,729✔
2914

2915
        group.add_argument("-exponent_X")
2,729✔
2916
            .append()
2,729✔
2917
            .metavar("<value>")
5,458✔
2918
            .scan<'g', double>()
2,729✔
2919
            .help(
2920
                _("Exponent to apply non-linear scaling with a power function, "
2921
                  "for band X"));
2,729✔
2922
    }
2923

2924
    argParser->add_argument("-srcwin")
2,729✔
2925
        .metavar("<xoff> <yoff> <xsize> <ysize>")
5,458✔
2926
        .nargs(4)
2,729✔
2927
        .scan<'g', double>()
2,729✔
2928
        .help(_("Selects a subwindow from the source image based on pixel/line "
2929
                "location."));
2,729✔
2930

2931
    argParser->add_argument("-projwin")
2,729✔
2932
        .metavar("<ulx> <uly> <lrx> <lry>")
5,458✔
2933
        .nargs(4)
2,729✔
2934
        .scan<'g', double>()
2,729✔
2935
        .help(_("Selects a subwindow from the source image based on "
2936
                "georeferenced coordinates."));
2,729✔
2937

2938
    argParser->add_argument("-projwin_srs")
2,729✔
2939
        .metavar("<srs_def>")
5,458✔
2940
        .store_into(psOptions->osProjSRS)
2,729✔
2941
        .help(_("Specifies the SRS in which to interpret the coordinates given "
2942
                "with -projwin."));
2,729✔
2943

2944
    argParser->add_argument("-epo")
2,729✔
2945
        .flag()
2,729✔
2946
        .action(
2947
            [psOptions](const std::string &)
12✔
2948
            {
2949
                psOptions->bErrorOnPartiallyOutside = true;
12✔
2950
                psOptions->bErrorOnCompletelyOutside = true;
12✔
2951
            })
2,729✔
2952
        .help(_("Error when Partially Outside."));
2,729✔
2953

2954
    argParser->add_argument("-eco")
2,729✔
2955
        .store_into(psOptions->bErrorOnCompletelyOutside)
2,729✔
2956
        .help(_("Error when Completely Outside."));
2,729✔
2957

2958
    argParser->add_argument("-a_srs")
2,729✔
2959
        .metavar("<srs_def>")
5,458✔
2960
        .store_into(psOptions->osOutputSRS)
2,729✔
2961
        .help(_("Override the projection for the output file."));
2,729✔
2962

2963
    argParser->add_argument("-a_coord_epoch")
2,729✔
2964
        .metavar("<epoch>")
5,458✔
2965
        .store_into(psOptions->dfOutputCoordinateEpoch)
2,729✔
2966
        .help(_("Assign a coordinate epoch."));
2,729✔
2967

2968
    argParser->add_argument("-a_ullr")
2,729✔
2969
        .metavar("<ulx> <uly> <lrx> <lry>")
5,458✔
2970
        .nargs(4)
2,729✔
2971
        .scan<'g', double>()
2,729✔
2972
        .help(
2973
            _("Assign/override the georeferenced bounds of the output file."));
2,729✔
2974

2975
    argParser->add_argument("-a_nodata")
2,729✔
2976
        .metavar("<value>|none")
5,458✔
2977
        .help(_("Assign a specified nodata value to output bands."));
2,729✔
2978

2979
    argParser->add_argument("-a_gt")
2,729✔
2980
        .metavar("<gt(0)> <gt(1)> <gt(2)> <gt(3)> <gt(4)> <gt(5)>")
5,458✔
2981
        .nargs(6)
2,729✔
2982
        .scan<'g', double>()
2,729✔
2983
        .help(_("Assign/override the geotransform of the output file."));
2,729✔
2984

2985
    argParser->add_argument("-a_scale")
2,729✔
2986
        .metavar("<value>")
5,458✔
2987
        .store_into(psOptions->dfScale)
2,729✔
2988
        .help(_("Set band scaling value."));
2,729✔
2989

2990
    argParser->add_argument("-a_offset")
2,729✔
2991
        .metavar("<value>")
5,458✔
2992
        .store_into(psOptions->dfOffset)
2,729✔
2993
        .help(_("Set band offset value."));
2,729✔
2994

2995
    argParser->add_argument("-nogcp")
2,729✔
2996
        .store_into(psOptions->bNoGCP)
2,729✔
2997
        .help(_("Do not copy the GCPs in the source dataset to the output "
2998
                "dataset."));
2,729✔
2999

3000
    argParser->add_argument("-gcp")
2,729✔
3001
        .metavar("<pixel> <line> <easting> <northing> [<elevation>]")
5,458✔
3002
        .nargs(4, 5)
2,729✔
3003
        .append()
2,729✔
3004
        .scan<'g', double>()
2,729✔
3005
        .help(
3006
            _("Add the indicated ground control point to the output dataset."));
2,729✔
3007

3008
    argParser->add_argument("-colorinterp")
2,729✔
3009
        .metavar("{red|green|blue|alpha|gray|undefined|pan|coastal|rededge|nir|"
3010
                 "swir|mwir|lwir|...},...")
5,458✔
3011
        .action(
3012
            [psOptions](const std::string &s)
26✔
3013
            {
3014
                CPLStringList aosList(CSLTokenizeString2(s.c_str(), ",", 0));
10✔
3015
                psOptions->anColorInterp.resize(aosList.size());
5✔
3016
                for (int j = 0; j < aosList.size(); j++)
21✔
3017
                {
3018
                    psOptions->anColorInterp[j] = GetColorInterp(aosList[j]);
16✔
3019
                }
3020
            })
2,734✔
3021
        .help(_("Override the color interpretation of all specified bands."));
2,729✔
3022

3023
    argParser->add_argument("-colorinterp_X")
2,729✔
3024
        .append()
2,729✔
3025
        .metavar("{red|green|blue|alpha|gray|undefined|pan|coastal|rededge|nir|"
3026
                 "swir|mwir|lwir|...}")
5,458✔
3027
        .help(_("Override the color interpretation of band X."));
2,729✔
3028

3029
    {
3030
        auto &group = argParser->add_mutually_exclusive_group();
2,729✔
3031
        group.add_argument("-stats")
2,729✔
3032
            .flag()
2,729✔
3033
            .action(
3034
                [psOptions](const std::string &)
4✔
3035
                {
3036
                    psOptions->bStats = true;
4✔
3037
                    psOptions->bApproxStats = false;
4✔
3038
                })
2,729✔
3039
            .help(_("Force (re)computation of statistics."));
2,729✔
3040

3041
        group.add_argument("-approx_stats")
2,729✔
3042
            .flag()
2,729✔
3043
            .action(
3044
                [psOptions](const std::string &)
×
3045
                {
3046
                    psOptions->bStats = true;
×
3047
                    psOptions->bApproxStats = true;
×
3048
                })
2,729✔
3049
            .help(_("Force (re)computation of approximate statistics."));
2,729✔
3050
    }
3051

3052
    argParser->add_argument("-norat")
2,729✔
3053
        .store_into(psOptions->bNoRAT)
2,729✔
3054
        .help(_("Do not copy source RAT into destination dataset."));
2,729✔
3055

3056
    argParser->add_argument("-noxmp")
2,729✔
3057
        .store_into(psOptions->bNoXMP)
2,729✔
3058
        .help(_("Do not copy the XMP metadata into destination dataset."));
2,729✔
3059

3060
    argParser->add_creation_options_argument(psOptions->aosCreateOptions);
2,729✔
3061

3062
    argParser->add_metadata_item_options_argument(
3063
        psOptions->aosMetadataOptions);
2,729✔
3064

3065
    argParser->add_argument("-dmo")
2,729✔
3066
        .metavar("<DOMAIN>:<KEY>=<VALUE>")
5,458✔
3067
        .append()
2,729✔
3068
        .action([psOptions](const std::string &s)
9✔
3069
                { psOptions->aosDomainMetadataOptions.AddString(s.c_str()); })
2,738✔
3070
        .help(_("Passes a metadata key and value in specified domain to set on "
3071
                "the output dataset if possible."));
2,729✔
3072

3073
    argParser->add_open_options_argument(
3074
        psOptionsForBinary ? &(psOptionsForBinary->aosOpenOptions) : nullptr);
2,729✔
3075

3076
    // Undocumented option used by gdal_translate_fuzzer
3077
    argParser->add_argument("-limit_outsize")
2,729✔
3078
        .hidden()
2,729✔
3079
        .store_into(psOptions->nLimitOutSize);
2,729✔
3080

3081
    // Undocumented option used by gdal raster convert
3082
    argParser->add_argument("--no-overwrite")
2,729✔
3083
        .store_into(psOptions->bNoOverwrite)
2,729✔
3084
        .hidden();
2,729✔
3085

3086
    // Undocumented option used by gdal raster scale
3087
    argParser->add_argument("--no-clip")
2,729✔
3088
        .store_into(psOptions->bNoClip)
2,729✔
3089
        .hidden();
2,729✔
3090

3091
    if (psOptionsForBinary)
2,729✔
3092
    {
3093
        argParser->add_argument("input_file")
143✔
3094
            .metavar("<input_file>")
286✔
3095
            .store_into(psOptionsForBinary->osSource)
143✔
3096
            .help(_("Input file."));
143✔
3097

3098
        argParser->add_argument("output_file")
143✔
3099
            .metavar("<output_file>")
286✔
3100
            .store_into(psOptionsForBinary->osDest)
143✔
3101
            .help(_("Output file."));
143✔
3102
    }
3103

3104
    return argParser;
2,729✔
3105
}
3106

3107
/************************************************************************/
3108
/*                      GDALTranslateGetParserUsage()                   */
3109
/************************************************************************/
3110

3111
std::string GDALTranslateGetParserUsage()
4✔
3112
{
3113
    try
3114
    {
3115
        GDALTranslateOptions sOptions;
8✔
3116
        auto argParser = GDALTranslateOptionsGetParser(&sOptions, nullptr);
8✔
3117
        return argParser->usage();
4✔
3118
    }
3119
    catch (const std::exception &err)
×
3120
    {
3121
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
×
3122
                 err.what());
×
3123
        return std::string();
×
3124
    }
3125
}
3126

3127
/************************************************************************/
3128
/*                             GDALTranslateOptionsNew()                */
3129
/************************************************************************/
3130

3131
/**
3132
 * Allocates a GDALTranslateOptions struct.
3133
 *
3134
 * @param papszArgv NULL terminated list of options (potentially including
3135
 * filename and open options too), or NULL. The accepted options are the ones of
3136
 * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
3137
 * @param psOptionsForBinary (output) may be NULL (and should generally be
3138
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
3139
 *                           GDALTranslateOptionsForBinaryNew() prior to this
3140
 * function. Will be filled with potentially present filename, open options,...
3141
 * @return pointer to the allocated GDALTranslateOptions struct. Must be freed
3142
 * with GDALTranslateOptionsFree().
3143
 *
3144
 * @since GDAL 2.1
3145
 */
3146

3147
GDALTranslateOptions *
3148
GDALTranslateOptionsNew(char **papszArgv,
2,727✔
3149
                        GDALTranslateOptionsForBinary *psOptionsForBinary)
3150
{
3151
    auto psOptions = std::make_unique<GDALTranslateOptions>();
5,454✔
3152

3153
    /* -------------------------------------------------------------------- */
3154
    /*      Pre-processing for custom syntax that ArgumentParser does not   */
3155
    /*      support.                                                        */
3156
    /* -------------------------------------------------------------------- */
3157

3158
    CPLStringList aosArgv;
5,454✔
3159
    const int argc = CSLCount(papszArgv);
2,727✔
3160
    for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
16,420✔
3161
         i++)
3162
    {
3163
        if (i + 4 < argc && EQUAL(papszArgv[i], "-gcp"))
13,695✔
3164
        {
3165
            /* -gcp pixel line easting northing [elev] */
3166
            psOptions->asGCPs.resize(psOptions->asGCPs.size() + 1);
29✔
3167
            psOptions->asGCPs.back().Pixel() = CPLAtofM(papszArgv[++i]);
29✔
3168
            psOptions->asGCPs.back().Line() = CPLAtofM(papszArgv[++i]);
29✔
3169
            psOptions->asGCPs.back().X() = CPLAtofM(papszArgv[++i]);
29✔
3170
            psOptions->asGCPs.back().Y() = CPLAtofM(papszArgv[++i]);
29✔
3171

3172
            char *endptr = nullptr;
29✔
3173
            if (papszArgv[i + 1] != nullptr &&
56✔
3174
                (CPLStrtod(papszArgv[i + 1], &endptr) != 0.0 ||
27✔
3175
                 papszArgv[i + 1][0] == '0'))
27✔
3176
            {
3177
                /* Check that last argument is really a number and not a
3178
                 * filename */
3179
                /* looking like a number (see ticket #863) */
3180
                if (endptr && *endptr == 0)
13✔
3181
                    psOptions->asGCPs.back().Z() = CPLAtofM(papszArgv[++i]);
13✔
3182
            }
29✔
3183

3184
            /* should set id and info? */
3185
        }
3186

3187
        else if (EQUAL(papszArgv[i], "-scale") ||
13,666✔
3188
                 STARTS_WITH_CI(papszArgv[i], "-scale_"))
13,610✔
3189
        {
3190
            int nIndex = 0;
69✔
3191
            if (STARTS_WITH_CI(papszArgv[i], "-scale_"))
69✔
3192
            {
3193
                if (!psOptions->bHasUsedExplicitScaleBand &&
26✔
3194
                    !psOptions->asScaleParams.empty())
13✔
3195
                {
3196
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3197
                             "Cannot mix -scale and -scale_XX syntax");
3198
                    return nullptr;
×
3199
                }
3200
                psOptions->bHasUsedExplicitScaleBand = true;
13✔
3201
                nIndex = atoi(papszArgv[i] + 7);
13✔
3202
                if (nIndex <= 0 || nIndex > 65535)
13✔
3203
                {
3204
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3205
                             "Invalid parameter name: %s", papszArgv[i]);
×
3206
                    return nullptr;
×
3207
                }
3208
                nIndex--;
13✔
3209
            }
3210
            else
3211
            {
3212
                if (psOptions->bHasUsedExplicitScaleBand)
56✔
3213
                {
3214
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3215
                             "Cannot mix -scale and -scale_XX syntax");
3216
                    return nullptr;
×
3217
                }
3218
                nIndex = static_cast<int>(psOptions->asScaleParams.size());
56✔
3219
            }
3220

3221
            if (nIndex >= static_cast<int>(psOptions->asScaleParams.size()))
69✔
3222
            {
3223
                psOptions->asScaleParams.resize(nIndex + 1);
69✔
3224
            }
3225
            psOptions->asScaleParams[nIndex].bScale = true;
69✔
3226
            bool bScanForDst = false;
69✔
3227
            if (i < argc - 2 && EQUAL(papszArgv[i + 1], "NaN") &&
69✔
3228
                EQUAL(papszArgv[i + 2], "NaN"))
1✔
3229
            {
3230
                bScanForDst = true;
1✔
3231
                i += 2;
1✔
3232
            }
3233
            else if (i < argc - 2 && ArgIsNumeric(papszArgv[i + 1]))
68✔
3234
            {
3235
                if (!ArgIsNumeric(papszArgv[i + 2]))
56✔
3236
                {
3237
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
3238
                             "Value of -scale must be numeric");
3239
                    return nullptr;
×
3240
                }
3241
                psOptions->asScaleParams[nIndex].dfScaleSrcMin =
56✔
3242
                    CPLAtofM(papszArgv[i + 1]);
56✔
3243
                psOptions->asScaleParams[nIndex].dfScaleSrcMax =
56✔
3244
                    CPLAtofM(papszArgv[i + 2]);
56✔
3245
                bScanForDst = true;
56✔
3246
                i += 2;
56✔
3247
            }
3248
            if (i < argc - 2 && bScanForDst && ArgIsNumeric(papszArgv[i + 1]))
69✔
3249
            {
3250
                if (!ArgIsNumeric(papszArgv[i + 2]))
53✔
3251
                {
3252
                    CPLError(CE_Failure, CPLE_IllegalArg,
1✔
3253
                             "Value of -scale must be numeric");
3254
                    return nullptr;
1✔
3255
                }
3256
                psOptions->asScaleParams[nIndex].dfScaleDstMin =
52✔
3257
                    CPLAtofM(papszArgv[i + 1]);
52✔
3258
                psOptions->asScaleParams[nIndex].dfScaleDstMax =
52✔
3259
                    CPLAtofM(papszArgv[i + 2]);
52✔
3260
                i += 2;
52✔
3261
            }
68✔
3262
        }
3263

3264
        else if ((EQUAL(papszArgv[i], "-exponent") ||
13,597✔
3265
                  STARTS_WITH_CI(papszArgv[i], "-exponent_")) &&
13,577✔
3266
                 papszArgv[i + 1])
24✔
3267
        {
3268
            int nIndex = 0;
24✔
3269
            if (STARTS_WITH_CI(papszArgv[i], "-exponent_"))
24✔
3270
            {
3271
                if (!psOptions->bHasUsedExplicitExponentBand &&
8✔
3272
                    !psOptions->adfExponent.empty())
4✔
3273
                {
3274
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3275
                             "Cannot mix -exponent and -exponent_XX syntax");
3276
                    return nullptr;
×
3277
                }
3278
                psOptions->bHasUsedExplicitExponentBand = true;
4✔
3279
                nIndex = atoi(papszArgv[i] + 10);
4✔
3280
                if (nIndex <= 0 || nIndex > 65535)
4✔
3281
                {
3282
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3283
                             "Invalid parameter name: %s", papszArgv[i]);
×
3284
                    return nullptr;
×
3285
                }
3286
                nIndex--;
4✔
3287
            }
3288
            else
3289
            {
3290
                if (psOptions->bHasUsedExplicitExponentBand)
20✔
3291
                {
3292
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3293
                             "Cannot mix -exponent and -exponent_XX syntax");
3294
                    return nullptr;
×
3295
                }
3296
                nIndex = static_cast<int>(psOptions->adfExponent.size());
20✔
3297
            }
3298

3299
            if (nIndex >= static_cast<int>(psOptions->adfExponent.size()))
24✔
3300
            {
3301
                psOptions->adfExponent.resize(nIndex + 1);
24✔
3302
            }
3303
            double dfExponent = CPLAtofM(papszArgv[++i]);
24✔
3304
            psOptions->adfExponent[nIndex] = dfExponent;
24✔
3305
        }
3306

3307
        else if (STARTS_WITH_CI(papszArgv[i], "-colorinterp_") &&
13,573✔
3308
                 papszArgv[i + 1])
14✔
3309
        {
3310
            int nIndex = atoi(papszArgv[i] + strlen("-colorinterp_"));
14✔
3311
            if (nIndex <= 0 || nIndex > 65535)
14✔
3312
            {
3313
                CPLError(CE_Failure, CPLE_NotSupported,
1✔
3314
                         "Invalid parameter name: %s", papszArgv[i]);
1✔
3315
                return nullptr;
1✔
3316
            }
3317
            nIndex--;
13✔
3318

3319
            if (nIndex >= static_cast<int>(psOptions->anColorInterp.size()))
13✔
3320
            {
3321
                psOptions->anColorInterp.resize(nIndex + 1, -1);
13✔
3322
            }
3323
            ++i;
13✔
3324
            psOptions->anColorInterp[nIndex] = GetColorInterp(papszArgv[i]);
13✔
3325
        }
3326

3327
        // argparser will be confused if the value of a string argument
3328
        // starts with a negative sign.
3329
        else if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
13,559✔
3330
        {
3331
            ++i;
74✔
3332
            const char *s = papszArgv[i];
74✔
3333
            if (EQUAL(s, "none") || EQUAL(s, "null"))
74✔
3334
            {
3335
                psOptions->bUnsetNoData = true;
4✔
3336
            }
3337
            else
3338
            {
3339
                psOptions->bSetNoData = true;
70✔
3340
                psOptions->osNoData = s;
70✔
3341
            }
74✔
3342
        }
3343

3344
        else
3345
        {
3346
            aosArgv.AddString(papszArgv[i]);
13,485✔
3347
        }
3348
    }
3349

3350
    try
3351
    {
3352

3353
        auto argParser =
3354
            GDALTranslateOptionsGetParser(psOptions.get(), psOptionsForBinary);
5,450✔
3355

3356
        argParser->parse_args_without_binary_name(aosArgv.List());
2,725✔
3357

3358
        psOptions->bSetScale = argParser->is_used("-a_scale");
2,721✔
3359
        psOptions->bSetOffset = argParser->is_used("-a_offset");
2,721✔
3360

3361
        if (auto adfULLR = argParser->present<std::vector<double>>("-a_ullr"))
2,740✔
3362
        {
3363
            CPLAssert(psOptions->adfULLR.size() == adfULLR->size());
19✔
3364
            for (size_t i = 0; i < adfULLR->size(); ++i)
95✔
3365
                psOptions->adfULLR[i] = (*adfULLR)[i];
76✔
3366
        }
3367

3368
        if (auto adfGT = argParser->present<std::vector<double>>("-a_gt"))
2,724✔
3369
        {
3370
            CPLAssert(adfGT->size() == 6);
3✔
3371
            for (size_t i = 0; i < adfGT->size(); ++i)
21✔
3372
                psOptions->gt[i] = (*adfGT)[i];
18✔
3373
        }
3374

3375
        bool bOutsizeExplicitlySet = false;
2,721✔
3376
        if (auto aosOutSize =
2,721✔
3377
                argParser->present<std::vector<std::string>>("-outsize"))
5,442✔
3378
        {
3379
            if ((*aosOutSize)[0].back() == '%')
229✔
3380
                psOptions->dfOXSizePct = CPLAtofM((*aosOutSize)[0].c_str());
30✔
3381
            else
3382
                psOptions->nOXSizePixel = atoi((*aosOutSize)[0].c_str());
199✔
3383

3384
            if ((*aosOutSize)[1].back() == '%')
229✔
3385
                psOptions->dfOYSizePct = CPLAtofM((*aosOutSize)[1].c_str());
29✔
3386
            else
3387
                psOptions->nOYSizePixel = atoi((*aosOutSize)[1].c_str());
200✔
3388
            bOutsizeExplicitlySet = true;
229✔
3389
        }
3390

3391
        if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
2,721✔
3392
        {
3393
            psOptions->dfXRes = (*adfTargetRes)[0];
12✔
3394
            psOptions->dfYRes = fabs((*adfTargetRes)[1]);
12✔
3395
            if (psOptions->dfXRes == 0 || psOptions->dfYRes == 0)
12✔
3396
            {
3397
                CPLError(CE_Failure, CPLE_IllegalArg,
×
3398
                         "Wrong value for -tr parameters.");
3399
                return nullptr;
×
3400
            }
3401
        }
3402

3403
        if (auto adfSrcWin = argParser->present<std::vector<double>>("-srcwin"))
3,630✔
3404
        {
3405
            psOptions->srcWin.dfXOff = (*adfSrcWin)[0];
909✔
3406
            psOptions->srcWin.dfYOff = (*adfSrcWin)[1];
909✔
3407
            psOptions->srcWin.dfXSize = (*adfSrcWin)[2];
909✔
3408
            psOptions->srcWin.dfYSize = (*adfSrcWin)[3];
909✔
3409
        }
3410

3411
        if (auto adfProjWin =
2,721✔
3412
                argParser->present<std::vector<double>>("-projwin"))
5,442✔
3413
        {
3414
            psOptions->dfULX = (*adfProjWin)[0];
189✔
3415
            psOptions->dfULY = (*adfProjWin)[1];
189✔
3416
            psOptions->dfLRX = (*adfProjWin)[2];
189✔
3417
            psOptions->dfLRY = (*adfProjWin)[3];
189✔
3418
        }
3419

3420
        if (!psOptions->asGCPs.empty() && psOptions->bNoGCP)
2,721✔
3421
        {
3422
            CPLError(CE_Failure, CPLE_IllegalArg,
×
3423
                     "-nogcp and -gcp cannot be used as the same time");
3424
            return nullptr;
×
3425
        }
3426

3427
        if (bOutsizeExplicitlySet && psOptions->nOXSizePixel == 0 &&
229✔
3428
            psOptions->dfOXSizePct == 0.0 && psOptions->nOYSizePixel == 0 &&
2,951✔
3429
            psOptions->dfOYSizePct == 0.0)
1✔
3430
        {
3431
            CPLError(CE_Failure, CPLE_NotSupported, "-outsize %d %d invalid.",
1✔
3432
                     psOptions->nOXSizePixel, psOptions->nOYSizePixel);
1✔
3433
            return nullptr;
1✔
3434
        }
3435

3436
        if (!psOptions->asScaleParams.empty() && psOptions->bUnscale)
2,720✔
3437
        {
3438
            CPLError(CE_Failure, CPLE_IllegalArg,
1✔
3439
                     "-scale and -unscale cannot be used as the same time");
3440
            return nullptr;
1✔
3441
        }
3442

3443
        if (psOptionsForBinary)
2,719✔
3444
        {
3445
            psOptionsForBinary->bQuiet = psOptions->bQuiet;
140✔
3446
            psOptionsForBinary->aosCreateOptions = psOptions->aosCreateOptions;
140✔
3447
            if (!psOptions->osFormat.empty())
140✔
3448
                psOptionsForBinary->osFormat = psOptions->osFormat;
83✔
3449
        }
3450

3451
        return psOptions.release();
2,719✔
3452
    }
3453
    catch (const std::exception &err)
4✔
3454
    {
3455
        CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
4✔
3456
        return nullptr;
4✔
3457
    }
3458
}
3459

3460
/************************************************************************/
3461
/*                        GDALTranslateOptionsFree()                    */
3462
/************************************************************************/
3463

3464
/**
3465
 * Frees the GDALTranslateOptions struct.
3466
 *
3467
 * @param psOptions the options struct for GDALTranslate().
3468
 *
3469
 * @since GDAL 2.1
3470
 */
3471

3472
void GDALTranslateOptionsFree(GDALTranslateOptions *psOptions)
2,703✔
3473
{
3474
    delete psOptions;
2,703✔
3475
}
2,703✔
3476

3477
/************************************************************************/
3478
/*                 GDALTranslateOptionsSetProgress()                    */
3479
/************************************************************************/
3480

3481
/**
3482
 * Set a progress function.
3483
 *
3484
 * @param psOptions the options struct for GDALTranslate().
3485
 * @param pfnProgress the progress callback.
3486
 * @param pProgressData the user data for the progress callback.
3487
 *
3488
 * @since GDAL 2.1
3489
 */
3490

3491
void GDALTranslateOptionsSetProgress(GDALTranslateOptions *psOptions,
388✔
3492
                                     GDALProgressFunc pfnProgress,
3493
                                     void *pProgressData)
3494
{
3495
    psOptions->pfnProgress = pfnProgress;
388✔
3496
    psOptions->pProgressData = pProgressData;
388✔
3497
    if (pfnProgress == GDALTermProgress)
388✔
3498
        psOptions->bQuiet = false;
129✔
3499
}
388✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc