Coverage Report

Created: 2025-06-13 06:29

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