Coverage Report

Created: 2025-12-31 06:48

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