Coverage Report

Created: 2025-06-22 06:59

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