Coverage Report

Created: 2026-07-14 06:21

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.xscale == 0.0 || gt.yscale == 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.IsAxisAligned())
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.xorig) / gt.xscale;
975
0
        psOptions->srcWin.dfYOff = (dfULY - gt.yorig) / gt.yscale;
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.xscale + gt.xorig;
986
0
            dfULY = psOptions->srcWin.dfYOff * gt.yscale + gt.yorig;
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.xscale;  // xsize
992
0
        psOptions->srcWin.dfYSize =
993
0
            (psOptions->dfLRY - dfULY) / gt.yscale;  // 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
0
    }
1003
1004
    /* -------------------------------------------------------------------- */
1005
    /*      Verify source window dimensions.                                */
1006
    /* -------------------------------------------------------------------- */
1007
0
    if (poSrcDS->GetRasterXSize() != 0 && poSrcDS->GetRasterYSize() != 0 &&
1008
0
        (psOptions->srcWin.dfXSize <= 0 || psOptions->srcWin.dfYSize <= 0))
1009
0
    {
1010
0
        CPLError(CE_Failure, CPLE_AppDefined,
1011
0
                 "Error: %ssource window (x,y)=(%g,%g), (width,height)=(%g,%g) "
1012
0
                 "has negative width and/or height.",
1013
0
                 (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
1014
0
                  psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
1015
0
                     ? "Computed "
1016
0
                     : "",
1017
0
                 psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
1018
0
                 psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize);
1019
0
        return nullptr;
1020
0
    }
1021
1022
    /* -------------------------------------------------------------------- */
1023
    /*      Verify source window dimensions.                                */
1024
    /* -------------------------------------------------------------------- */
1025
0
    else if (psOptions->srcWin.dfXOff <= -1 || psOptions->srcWin.dfYOff <= -1 ||
1026
0
             psOptions->srcWin.dfXOff + psOptions->srcWin.dfXSize - 1 >=
1027
0
                 poSrcDS->GetRasterXSize() ||
1028
0
             psOptions->srcWin.dfYOff + psOptions->srcWin.dfYSize - 1 >=
1029
0
                 poSrcDS->GetRasterYSize())
1030
0
    {
1031
0
        const bool bCompletelyOutside =
1032
0
            psOptions->srcWin.dfXOff + psOptions->srcWin.dfXSize <= 0 ||
1033
0
            psOptions->srcWin.dfYOff + psOptions->srcWin.dfYSize <= 0 ||
1034
0
            psOptions->srcWin.dfXOff >= poSrcDS->GetRasterXSize() ||
1035
0
            psOptions->srcWin.dfYOff >= poSrcDS->GetRasterYSize();
1036
0
        const bool bIsError =
1037
0
            psOptions->bErrorOnPartiallyOutside ||
1038
0
            (bCompletelyOutside && psOptions->bErrorOnCompletelyOutside);
1039
0
        if ((!psOptions->bQuiet && !psOptions->bNoWarnAboutOutsideWindow) ||
1040
0
            bIsError)
1041
0
        {
1042
0
            CPLErr eErr = bIsError ? CE_Failure : CE_Warning;
1043
1044
0
            CPLError(
1045
0
                eErr, CPLE_AppDefined,
1046
0
                "%ssource window %g %g %g %g falls %s outside source raster "
1047
0
                "extent.%s",
1048
0
                (psOptions->dfULX != 0.0 || psOptions->dfULY != 0.0 ||
1049
0
                 psOptions->dfLRX != 0.0 || psOptions->dfLRY != 0.0)
1050
0
                    ? "Computed "
1051
0
                    : "",
1052
0
                psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
1053
0
                psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize,
1054
0
                bCompletelyOutside ? "completely" : "partially",
1055
0
                bIsError ? ""
1056
0
                         : " Pixels outside the source raster extent will be "
1057
0
                           "set to the NoData value (if defined), or zero.");
1058
0
        }
1059
0
        if (bIsError)
1060
0
        {
1061
0
            return nullptr;
1062
0
        }
1063
0
    }
1064
1065
    /* -------------------------------------------------------------------- */
1066
    /*      Find the output driver.                                         */
1067
    /* -------------------------------------------------------------------- */
1068
0
    if (psOptions->osFormat.empty())
1069
0
    {
1070
0
        psOptions->osFormat = GetOutputDriverForRaster(pszDest);
1071
0
        if (psOptions->osFormat.empty())
1072
0
        {
1073
0
            CPLError(CE_Failure, CPLE_AppDefined,
1074
0
                     "Could not identify an output driver for %s", pszDest);
1075
0
            return nullptr;
1076
0
        }
1077
0
    }
1078
1079
0
    GDALDriverH hDriver = GDALGetDriverByName(psOptions->osFormat.c_str());
1080
0
    if (hDriver == nullptr)
1081
0
    {
1082
0
        CPLError(CE_Failure, CPLE_IllegalArg,
1083
0
                 "Output driver `%s' not recognised.",
1084
0
                 psOptions->osFormat.c_str());
1085
0
        return nullptr;
1086
0
    }
1087
1088
    /* -------------------------------------------------------------------- */
1089
    /*      Make sure we cleanup if there is an existing dataset of this    */
1090
    /*      name.  But even if that seems to fail we will continue since    */
1091
    /*      it might just be a corrupt file or something.                   */
1092
    /*      This is needed for                                              */
1093
    /*      gdal_translate foo.tif foo.tif.ovr -outsize 50% 50%             */
1094
    /* -------------------------------------------------------------------- */
1095
0
    if (psOptions->aosCreateOptions.FetchBool("APPEND_SUBDATASET", false))
1096
0
    {
1097
0
        if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE_SUBDATASETS,
1098
0
                                nullptr) == nullptr)
1099
0
        {
1100
0
            CPLError(CE_Failure, CPLE_NotSupported,
1101
0
                     "Subdataset creation not supported for driver %s",
1102
0
                     GDALGetDescription(hDriver));
1103
0
            return nullptr;
1104
0
        }
1105
0
    }
1106
0
    else
1107
0
    {
1108
0
        if (!EQUAL(psOptions->osFormat.c_str(), "VRT"))
1109
0
        {
1110
            // Prevent GDALDriver::CreateCopy() from doing that again.
1111
0
            psOptions->aosCreateOptions.SetNameValue(
1112
0
                "@QUIET_DELETE_ON_CREATE_COPY", "NO");
1113
0
        }
1114
1115
0
        if (psOptions->bNoOverwrite && !EQUAL(pszDest, "") &&
1116
0
            !EQUAL(pszDest, "/vsistdout/"))
1117
0
        {
1118
0
            VSIStatBufL sStat;
1119
0
            if (VSIStatL(pszDest, &sStat) == 0)
1120
0
            {
1121
0
                CPLError(CE_Failure, CPLE_AppDefined,
1122
0
                         "File '%s' already exists. Specify the --overwrite "
1123
0
                         "option to overwrite it.",
1124
0
                         pszDest);
1125
0
                return nullptr;
1126
0
            }
1127
0
            else
1128
0
            {
1129
0
                bool bExists;
1130
0
                {
1131
0
                    CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1132
0
                    bExists = std::unique_ptr<GDALDataset>(
1133
0
                                  GDALDataset::Open(pszDest)) != nullptr;
1134
0
                }
1135
0
                if (bExists)
1136
0
                {
1137
0
                    CPLError(
1138
0
                        CE_Failure, CPLE_AppDefined,
1139
0
                        "Dataset '%s' already exists. Specify the --overwrite "
1140
0
                        "option to overwrite it.",
1141
0
                        pszDest);
1142
0
                    return nullptr;
1143
0
                }
1144
0
            }
1145
0
        }
1146
1147
0
        GDALDriver::FromHandle(hDriver)->QuietDeleteForCreateCopy(pszDest,
1148
0
                                                                  poSrcDS);
1149
1150
        // Make sure to load early overviews, so that on the GTiff driver
1151
        // external .ovr is looked for before it might be created as the
1152
        // output dataset !
1153
0
        if (poSrcDS->GetRasterCount())
1154
0
        {
1155
0
            auto poBand = poSrcDS->GetRasterBand(1);
1156
0
            if (poBand)
1157
0
                poBand->GetOverviewCount();
1158
0
        }
1159
0
    }
1160
1161
0
    CSLConstList papszDriverMD = GDALGetMetadata(hDriver, nullptr);
1162
1163
0
    if (!CPLTestBool(
1164
0
            CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER, "FALSE")))
1165
0
    {
1166
0
        CPLError(CE_Failure, CPLE_AppDefined,
1167
0
                 "%s driver has no raster capabilities.",
1168
0
                 psOptions->osFormat.c_str());
1169
0
        return nullptr;
1170
0
    }
1171
1172
0
    if (!CPLTestBool(
1173
0
            CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")) &&
1174
0
        !CPLTestBool(
1175
0
            CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATECOPY, "FALSE")))
1176
0
    {
1177
0
        CPLError(CE_Failure, CPLE_AppDefined,
1178
0
                 "%s driver has no creation capabilities.",
1179
0
                 psOptions->osFormat.c_str());
1180
0
        return nullptr;
1181
0
    }
1182
1183
    /* -------------------------------------------------------------------- */
1184
    /*      The short form is to CreateCopy().  We use this if the input    */
1185
    /*      matches the whole dataset.  Eventually we should rewrite        */
1186
    /*      this entire program to use virtual datasets to construct a      */
1187
    /*      virtual input source to copy from.                              */
1188
    /* -------------------------------------------------------------------- */
1189
1190
0
    const bool bKeepResolution =
1191
0
        psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
1192
0
        psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0 &&
1193
0
        psOptions->dfXRes == 0.0;
1194
0
    const bool bKeepExtent =
1195
0
        psOptions->srcWin.dfXOff == 0 && psOptions->srcWin.dfYOff == 0 &&
1196
0
        psOptions->srcWin.dfXSize == poSrcDS->GetRasterXSize() &&
1197
0
        psOptions->srcWin.dfYSize == poSrcDS->GetRasterYSize();
1198
0
    const bool bSpatialArrangementPreserved = bKeepExtent && bKeepResolution;
1199
0
    const bool bValuesChanged =
1200
0
        psOptions->bUnscale || !psOptions->asScaleParams.empty();
1201
1202
0
    if (psOptions->eOutputType == GDT_Unknown &&
1203
0
        psOptions->asScaleParams.empty() && psOptions->adfExponent.empty() &&
1204
0
        !psOptions->bUnscale && !psOptions->bSetScale &&
1205
0
        !psOptions->bSetOffset && psOptions->aosMetadataOptions.empty() &&
1206
0
        psOptions->aosDomainMetadataOptions.empty() && bAllBandsInOrder &&
1207
0
        psOptions->eMaskMode == MASK_AUTO && bSpatialArrangementPreserved &&
1208
0
        !psOptions->bNoGCP && psOptions->asGCPs.empty() && !bGotBounds &&
1209
0
        !bGotGeoTransform && psOptions->osOutputSRS.empty() &&
1210
0
        psOptions->dfOutputCoordinateEpoch == 0 && !psOptions->bSetNoData &&
1211
0
        !psOptions->bUnsetNoData && psOptions->nRGBExpand == 0 &&
1212
0
        !psOptions->bNoRAT && psOptions->anColorInterp.empty() &&
1213
0
        !psOptions->bNoXMP && psOptions->nOvLevel == OVR_LEVEL_AUTO)
1214
0
    {
1215
1216
        // For gdal_translate_fuzzer
1217
0
        if (psOptions->nLimitOutSize > 0)
1218
0
        {
1219
0
            vsi_l_offset nRawOutSize =
1220
0
                static_cast<vsi_l_offset>(poSrcDS->GetRasterXSize()) *
1221
0
                poSrcDS->GetRasterYSize() * psOptions->nBandCount;
1222
0
            if (psOptions->nBandCount)
1223
0
            {
1224
0
                nRawOutSize *= GDALGetDataTypeSizeBytes(
1225
0
                    poSrcDS->GetRasterBand(1)->GetRasterDataType());
1226
0
            }
1227
0
            if (nRawOutSize >
1228
0
                static_cast<vsi_l_offset>(psOptions->nLimitOutSize))
1229
0
            {
1230
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1231
0
                         "Attempt to create %dx%d dataset is above authorized "
1232
0
                         "limit.",
1233
0
                         poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1234
0
                return nullptr;
1235
0
            }
1236
0
        }
1237
1238
        /* --------------------------------------------------------------------
1239
         */
1240
        /*      Compute stats if required. */
1241
        /* --------------------------------------------------------------------
1242
         */
1243
1244
0
        if (psOptions->bStats && EQUAL(psOptions->osFormat.c_str(), "COG"))
1245
0
        {
1246
0
            psOptions->aosCreateOptions.SetNameValue("STATISTICS", "YES");
1247
0
        }
1248
0
        else if (psOptions->bStats)
1249
0
        {
1250
0
            for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
1251
0
            {
1252
0
                double dfMin, dfMax, dfMean, dfStdDev;
1253
0
                poSrcDS->GetRasterBand(i + 1)->ComputeStatistics(
1254
0
                    psOptions->bApproxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
1255
0
                    GDALDummyProgress, nullptr, nullptr);
1256
0
            }
1257
0
        }
1258
1259
0
        hOutDS = GDALCreateCopy(
1260
0
            hDriver, pszDest, GDALDataset::ToHandle(poSrcDS),
1261
0
            psOptions->bStrict, psOptions->aosCreateOptions.List(),
1262
0
            psOptions->pfnProgress, psOptions->pProgressData);
1263
0
        hOutDS = GDALTranslateFlush(hOutDS);
1264
1265
0
        return hOutDS;
1266
0
    }
1267
1268
0
    if (psOptions->aosCreateOptions.FetchNameValue("COPY_SRC_OVERVIEWS"))
1269
0
    {
1270
0
        CPLError(CE_Warning, CPLE_AppDefined,
1271
0
                 "General options of gdal_translate make the "
1272
0
                 "COPY_SRC_OVERVIEWS creation option ineffective as they hide "
1273
0
                 "the overviews");
1274
0
    }
1275
1276
    /* -------------------------------------------------------------------- */
1277
    /*      Establish some parameters.                                      */
1278
    /* -------------------------------------------------------------------- */
1279
0
    int nOXSize = 0;
1280
0
    int nOYSize = 0;
1281
1282
0
    bool bHasSrcGeoTransform = false;
1283
0
    GDALGeoTransform srcGT;
1284
0
    if (poSrcDS->GetGeoTransform(srcGT) == CE_None)
1285
0
        bHasSrcGeoTransform = true;
1286
1287
0
    const bool bOutsizeExplicitlySet =
1288
0
        !(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0 &&
1289
0
          psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0);
1290
0
    if (psOptions->dfXRes != 0.0 && psOptions->dfYRes != 0.0)
1291
0
    {
1292
0
        if (!(bHasSrcGeoTransform && psOptions->asGCPs.empty() &&
1293
0
              srcGT[2] == 0.0 && srcGT[4] == 0.0))
1294
0
        {
1295
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1296
0
                     "The -tr option was used, but there's no geotransform or "
1297
0
                     "it is\n"
1298
0
                     "rotated.  This configuration is not supported.");
1299
0
            return nullptr;
1300
0
        }
1301
0
        const double dfOXSize =
1302
0
            psOptions->srcWin.dfXSize / psOptions->dfXRes * srcGT[1] + 0.5;
1303
0
        const double dfOYSize =
1304
0
            psOptions->srcWin.dfYSize / psOptions->dfYRes * fabs(srcGT[5]) +
1305
0
            0.5;
1306
0
        if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
1307
0
            dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
1308
0
        {
1309
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1310
0
                     "Invalid output size: %g x %g", dfOXSize, dfOYSize);
1311
0
            return nullptr;
1312
0
        }
1313
0
        nOXSize = static_cast<int>(dfOXSize);
1314
0
        nOYSize = static_cast<int>(dfOYSize);
1315
0
    }
1316
0
    else if (!bOutsizeExplicitlySet)
1317
0
    {
1318
0
        double dfOXSize = ceil(psOptions->srcWin.dfXSize - 0.001);
1319
0
        double dfOYSize = ceil(psOptions->srcWin.dfYSize - 0.001);
1320
0
        if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize) ||
1321
0
            dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
1322
0
        {
1323
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1324
0
                     "Invalid output size: %g x %g", dfOXSize, dfOYSize);
1325
0
            return nullptr;
1326
0
        }
1327
0
        nOXSize = static_cast<int>(dfOXSize);
1328
0
        nOYSize = static_cast<int>(dfOYSize);
1329
0
    }
1330
0
    else
1331
0
    {
1332
0
        if (!(psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0))
1333
0
        {
1334
0
            if (psOptions->nOXSizePixel != 0)
1335
0
                nOXSize = psOptions->nOXSizePixel;
1336
0
            else
1337
0
            {
1338
0
                const double dfOXSize =
1339
0
                    psOptions->dfOXSizePct / 100 * psOptions->srcWin.dfXSize;
1340
0
                if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize))
1341
0
                {
1342
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1343
0
                             "Invalid output width: %g", dfOXSize);
1344
0
                    return nullptr;
1345
0
                }
1346
0
                nOXSize = static_cast<int>(dfOXSize);
1347
0
            }
1348
0
        }
1349
1350
0
        if (!(psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0))
1351
0
        {
1352
0
            if (psOptions->nOYSizePixel != 0)
1353
0
                nOYSize = psOptions->nOYSizePixel;
1354
0
            else
1355
0
            {
1356
0
                const double dfOYSize =
1357
0
                    psOptions->dfOYSizePct / 100 * psOptions->srcWin.dfYSize;
1358
0
                if (dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
1359
0
                {
1360
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1361
0
                             "Invalid output height: %g", dfOYSize);
1362
0
                    return nullptr;
1363
0
                }
1364
0
                nOYSize = static_cast<int>(dfOYSize);
1365
0
            }
1366
0
        }
1367
1368
0
        if (psOptions->nOXSizePixel == 0 && psOptions->dfOXSizePct == 0.0)
1369
0
        {
1370
0
            const double dfOXSize = static_cast<double>(nOYSize) *
1371
0
                                        psOptions->srcWin.dfXSize /
1372
0
                                        psOptions->srcWin.dfYSize +
1373
0
                                    0.5;
1374
0
            if (dfOXSize < 1 || !GDALIsValueInRange<int>(dfOXSize))
1375
0
            {
1376
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1377
0
                         "Invalid output width: %g", dfOXSize);
1378
0
                return nullptr;
1379
0
            }
1380
0
            nOXSize = static_cast<int>(dfOXSize);
1381
0
        }
1382
0
        else if (psOptions->nOYSizePixel == 0 && psOptions->dfOYSizePct == 0.0)
1383
0
        {
1384
0
            const double dfOYSize = static_cast<double>(nOXSize) *
1385
0
                                        psOptions->srcWin.dfYSize /
1386
0
                                        psOptions->srcWin.dfXSize +
1387
0
                                    0.5;
1388
0
            if (dfOYSize < 1 || !GDALIsValueInRange<int>(dfOYSize))
1389
0
            {
1390
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1391
0
                         "Invalid output height: %g", dfOYSize);
1392
0
                return nullptr;
1393
0
            }
1394
0
            nOYSize = static_cast<int>(dfOYSize);
1395
0
        }
1396
0
    }
1397
1398
0
    if (nOXSize <= 0 || nOYSize <= 0)
1399
0
    {
1400
0
        CPLError(CE_Failure, CPLE_IllegalArg,
1401
0
                 "Attempt to create %dx%d dataset is illegal.", nOXSize,
1402
0
                 nOYSize);
1403
0
        return nullptr;
1404
0
    }
1405
1406
    // Build overview dataset if -ovr is specified
1407
0
    GDALDataset *poSrcOvrDS = nullptr;
1408
0
    GDALDataset *poSrcDSOri = poSrcDS;
1409
0
    const auto poFirstBand = poSrcDS->GetRasterBand(1);
1410
0
    const int nOvCount = poFirstBand ? poFirstBand->GetOverviewCount() : 0;
1411
0
    if (psOptions->nOvLevel < OVR_LEVEL_AUTO && poFirstBand && nOvCount > 0)
1412
0
    {
1413
0
        int iOvr = 0;
1414
0
        for (; iOvr < nOvCount - 1; iOvr++)
1415
0
        {
1416
0
            if (poFirstBand->GetOverview(iOvr)->GetXSize() <= nOXSize)
1417
0
            {
1418
0
                break;
1419
0
            }
1420
0
        }
1421
0
        iOvr += (psOptions->nOvLevel - OVR_LEVEL_AUTO);
1422
0
        if (iOvr >= 0)
1423
0
        {
1424
0
            CPLDebug("GDAL", "Selecting overview level %d", iOvr);
1425
0
            poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, iOvr,
1426
0
                                                   /* bThisLevelOnly = */ true);
1427
0
        }
1428
0
    }
1429
0
    else if (psOptions->nOvLevel >= OVR_LEVEL_NONE)
1430
0
    {
1431
0
        poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, psOptions->nOvLevel,
1432
0
                                               /* bThisLevelOnly = */ true);
1433
0
        if (poSrcOvrDS == nullptr)
1434
0
        {
1435
0
            if (!psOptions->bQuiet)
1436
0
            {
1437
0
                if (nOvCount > 0)
1438
0
                {
1439
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1440
0
                             "Cannot get overview level %d. "
1441
0
                             "Defaulting to level %d.",
1442
0
                             psOptions->nOvLevel, nOvCount - 1);
1443
0
                }
1444
0
                else
1445
0
                {
1446
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1447
0
                             "Cannot get overview level %d. "
1448
0
                             "Defaulting to full resolution.",
1449
0
                             psOptions->nOvLevel);
1450
0
                }
1451
0
            }
1452
0
            if (nOvCount > 0)
1453
0
                poSrcOvrDS =
1454
0
                    GDALCreateOverviewDataset(poSrcDS, nOvCount - 1,
1455
0
                                              /* bThisLevelOnly = */ true);
1456
0
        }
1457
0
        if (poSrcOvrDS && psOptions->dfXRes == 0.0 && !bOutsizeExplicitlySet)
1458
0
        {
1459
0
            const double dfRatioX =
1460
0
                static_cast<double>(poSrcDSOri->GetRasterXSize()) /
1461
0
                poSrcOvrDS->GetRasterXSize();
1462
0
            const double dfRatioY =
1463
0
                static_cast<double>(poSrcDSOri->GetRasterYSize()) /
1464
0
                poSrcOvrDS->GetRasterYSize();
1465
0
            nOXSize =
1466
0
                std::max(1, static_cast<int>(ceil(nOXSize / dfRatioX - 0.001)));
1467
0
            nOYSize =
1468
0
                std::max(1, static_cast<int>(ceil(nOYSize / dfRatioY - 0.001)));
1469
0
        }
1470
0
    }
1471
1472
0
    if (poSrcOvrDS)
1473
0
        poSrcDS = poSrcOvrDS;
1474
0
    else
1475
0
        poSrcDS->Reference();
1476
1477
    // For gdal_translate_fuzzer
1478
0
    if (psOptions->nLimitOutSize > 0)
1479
0
    {
1480
0
        vsi_l_offset nRawOutSize = static_cast<vsi_l_offset>(nOXSize) * nOYSize;
1481
0
        if (psOptions->nBandCount)
1482
0
        {
1483
0
            if (nRawOutSize > std::numeric_limits<vsi_l_offset>::max() /
1484
0
                                  psOptions->nBandCount)
1485
0
            {
1486
0
                poSrcDS->Release();
1487
0
                return nullptr;
1488
0
            }
1489
0
            nRawOutSize *= psOptions->nBandCount;
1490
0
            const int nDTSize = GDALGetDataTypeSizeBytes(
1491
0
                poSrcDS->GetRasterBand(1)->GetRasterDataType());
1492
0
            if (nDTSize > 0 &&
1493
0
                nRawOutSize >
1494
0
                    std::numeric_limits<vsi_l_offset>::max() / nDTSize)
1495
0
            {
1496
0
                poSrcDS->Release();
1497
0
                return nullptr;
1498
0
            }
1499
0
            nRawOutSize *= nDTSize;
1500
0
        }
1501
0
        if (nRawOutSize > static_cast<vsi_l_offset>(psOptions->nLimitOutSize))
1502
0
        {
1503
0
            CPLError(
1504
0
                CE_Failure, CPLE_IllegalArg,
1505
0
                "Attempt to create %dx%d dataset is above authorized limit.",
1506
0
                nOXSize, nOYSize);
1507
0
            poSrcDS->Release();
1508
0
            return nullptr;
1509
0
        }
1510
0
    }
1511
1512
    /* ==================================================================== */
1513
    /*      Create a virtual dataset.                                       */
1514
    /* ==================================================================== */
1515
1516
    /* -------------------------------------------------------------------- */
1517
    /*      Make a virtual clone.                                           */
1518
    /* -------------------------------------------------------------------- */
1519
0
    VRTDataset *poVDS = static_cast<VRTDataset *>(VRTCreate(nOXSize, nOYSize));
1520
1521
0
    if (psOptions->asGCPs.empty())
1522
0
    {
1523
0
        if (psOptions->osOutputSRS == "null" ||
1524
0
            psOptions->osOutputSRS == "none")
1525
0
        {
1526
0
            poVDS->SetSpatialRef(nullptr);
1527
0
        }
1528
0
        else
1529
0
        {
1530
0
            OGRSpatialReference oSRS;
1531
0
            if (!psOptions->osOutputSRS.empty())
1532
0
            {
1533
0
                oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
1534
0
                oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1535
0
            }
1536
0
            else
1537
0
            {
1538
0
                const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
1539
0
                if (poSrcSRS)
1540
0
                    oSRS = *poSrcSRS;
1541
0
            }
1542
0
            if (!oSRS.IsEmpty())
1543
0
            {
1544
0
                if (psOptions->dfOutputCoordinateEpoch > 0)
1545
0
                    oSRS.SetCoordinateEpoch(psOptions->dfOutputCoordinateEpoch);
1546
0
                poVDS->SetSpatialRef(&oSRS);
1547
0
            }
1548
0
        }
1549
0
    }
1550
1551
0
    bool bHasDstGeoTransform = false;
1552
0
    GDALGeoTransform dstGT;
1553
1554
0
    if (bGotBounds)
1555
0
    {
1556
0
        bHasDstGeoTransform = true;
1557
0
        dstGT[0] = psOptions->adfULLR[0];
1558
0
        dstGT[1] = (psOptions->adfULLR[2] - psOptions->adfULLR[0]) / nOXSize;
1559
0
        dstGT[2] = 0.0;
1560
0
        dstGT[3] = psOptions->adfULLR[1];
1561
0
        dstGT[4] = 0.0;
1562
0
        dstGT[5] = (psOptions->adfULLR[3] - psOptions->adfULLR[1]) / nOYSize;
1563
1564
0
        poVDS->SetGeoTransform(dstGT);
1565
0
    }
1566
1567
0
    else if (bGotGeoTransform)
1568
0
    {
1569
0
        bHasDstGeoTransform = true;
1570
0
        poVDS->SetGeoTransform(psOptions->gt);
1571
0
    }
1572
1573
0
    else if (bHasSrcGeoTransform && psOptions->asGCPs.empty())
1574
0
    {
1575
0
        bHasDstGeoTransform = true;
1576
0
        dstGT = srcGT;
1577
0
        dstGT[0] += psOptions->srcWin.dfXOff * dstGT[1] +
1578
0
                    psOptions->srcWin.dfYOff * dstGT[2];
1579
0
        dstGT[3] += psOptions->srcWin.dfXOff * dstGT[4] +
1580
0
                    psOptions->srcWin.dfYOff * dstGT[5];
1581
1582
0
        const double dfXRatio = psOptions->srcWin.dfXSize / nOXSize;
1583
0
        const double dfYRatio = psOptions->srcWin.dfYSize / nOYSize;
1584
0
        dstGT.Rescale(dfXRatio, dfYRatio);
1585
1586
0
        if (psOptions->dfXRes != 0.0)
1587
0
        {
1588
0
            dstGT[1] = psOptions->dfXRes;
1589
0
            dstGT[5] = (dstGT[5] > 0) ? psOptions->dfYRes : -psOptions->dfYRes;
1590
0
        }
1591
1592
0
        poVDS->SetGeoTransform(dstGT);
1593
0
    }
1594
1595
0
    if (!psOptions->asGCPs.empty())
1596
0
    {
1597
0
        OGRSpatialReference oSRS;
1598
0
        if (psOptions->osOutputSRS == "null" ||
1599
0
            psOptions->osOutputSRS == "none")
1600
0
        {
1601
            // nothing to do
1602
0
        }
1603
0
        else if (!psOptions->osOutputSRS.empty())
1604
0
        {
1605
0
            oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
1606
0
            oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1607
0
        }
1608
0
        else
1609
0
        {
1610
0
            const OGRSpatialReference *poSrcSRS = poSrcDS->GetGCPSpatialRef();
1611
0
            if (poSrcSRS)
1612
0
                oSRS = *poSrcSRS;
1613
0
        }
1614
0
        poVDS->SetGCPs(static_cast<int>(psOptions->asGCPs.size()),
1615
0
                       gdal::GCP::c_ptr(psOptions->asGCPs),
1616
0
                       !oSRS.IsEmpty() ? &oSRS : nullptr);
1617
0
    }
1618
1619
0
    else if (!psOptions->bNoGCP && poSrcDSOri->GetGCPCount() > 0)
1620
0
    {
1621
0
        const int nGCPs = poSrcDSOri->GetGCPCount();
1622
1623
0
        GDAL_GCP *pasGCPs = GDALDuplicateGCPs(nGCPs, poSrcDSOri->GetGCPs());
1624
1625
0
        for (int i = 0; i < nGCPs; i++)
1626
0
        {
1627
0
            pasGCPs[i].dfGCPPixel -= psOptions->srcWin.dfXOff;
1628
0
            pasGCPs[i].dfGCPLine -= psOptions->srcWin.dfYOff;
1629
0
            pasGCPs[i].dfGCPPixel *=
1630
0
                nOXSize / static_cast<double>(psOptions->srcWin.dfXSize);
1631
0
            pasGCPs[i].dfGCPLine *=
1632
0
                nOYSize / static_cast<double>(psOptions->srcWin.dfYSize);
1633
0
        }
1634
1635
0
        poVDS->SetGCPs(nGCPs, pasGCPs, poSrcDSOri->GetGCPSpatialRef());
1636
1637
0
        GDALDeinitGCPs(nGCPs, pasGCPs);
1638
0
        CPLFree(pasGCPs);
1639
0
    }
1640
1641
    /* -------------------------------------------------------------------- */
1642
    /*      To make the VRT to look less awkward (but this is optional      */
1643
    /*      in fact), avoid negative values.                                */
1644
    /* -------------------------------------------------------------------- */
1645
0
    GDALTranslateOptions::PixelLineWindow dstWin{
1646
0
        0.0, 0.0, static_cast<double>(nOXSize), static_cast<double>(nOYSize)};
1647
1648
    // When specifying -tr with non-nearest resampling, make sure that the
1649
    // size of target window precisely matches the requested resolution, to
1650
    // avoid any shift.
1651
0
    if (bHasSrcGeoTransform && bHasDstGeoTransform &&
1652
0
        psOptions->dfXRes != 0.0 && !psOptions->osResampling.empty() &&
1653
0
        !EQUALN(psOptions->osResampling.c_str(), "NEAR", 4))
1654
0
    {
1655
0
        dstWin.dfXSize = psOptions->srcWin.dfXSize * srcGT[1] / dstGT[1];
1656
0
        dstWin.dfYSize = psOptions->srcWin.dfYSize * fabs(srcGT[5] / dstGT[5]);
1657
0
    }
1658
1659
0
    GDALTranslateOptions::PixelLineWindow srcWinOri(psOptions->srcWin);
1660
0
    const double dfRatioX =
1661
0
        poSrcDS->GetRasterXSize() == 0
1662
0
            ? 1.0
1663
0
            : static_cast<double>(poSrcDSOri->GetRasterXSize()) /
1664
0
                  poSrcDS->GetRasterXSize();
1665
0
    const double dfRatioY =
1666
0
        poSrcDS->GetRasterYSize() == 0
1667
0
            ? 1.0
1668
0
            : static_cast<double>(poSrcDSOri->GetRasterYSize()) /
1669
0
                  poSrcDS->GetRasterYSize();
1670
0
    psOptions->srcWin.dfXOff /= dfRatioX;
1671
0
    psOptions->srcWin.dfYOff /= dfRatioY;
1672
0
    psOptions->srcWin.dfXSize /= dfRatioX;
1673
0
    psOptions->srcWin.dfYSize /= dfRatioY;
1674
0
    FixSrcDstWindow(psOptions->srcWin, dstWin, poSrcDS->GetRasterXSize(),
1675
0
                    poSrcDS->GetRasterYSize());
1676
1677
    /* -------------------------------------------------------------------- */
1678
    /*      Transfer generally applicable metadata.                         */
1679
    /* -------------------------------------------------------------------- */
1680
0
    char **papszMetadata = CSLDuplicate(poSrcDS->GetMetadata());
1681
0
    if (!psOptions->asScaleParams.empty() || psOptions->bUnscale ||
1682
0
        psOptions->eOutputType != GDT_Unknown)
1683
0
    {
1684
        /* Remove TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */
1685
        /* if the data range may change because of options */
1686
0
        char **papszIter = papszMetadata;
1687
0
        while (papszIter && *papszIter)
1688
0
        {
1689
0
            if (STARTS_WITH_CI(*papszIter, "TIFFTAG_MINSAMPLEVALUE=") ||
1690
0
                STARTS_WITH_CI(*papszIter, "TIFFTAG_MAXSAMPLEVALUE="))
1691
0
            {
1692
0
                CPLFree(*papszIter);
1693
0
                memmove(papszIter, papszIter + 1,
1694
0
                        sizeof(char *) * (CSLCount(papszIter + 1) + 1));
1695
0
            }
1696
0
            else
1697
0
                papszIter++;
1698
0
        }
1699
0
    }
1700
1701
    // Remove NITF_BLOCKA_ stuff if georeferencing is changed
1702
0
    if (!(psOptions->srcWin.dfXOff == 0 && psOptions->srcWin.dfYOff == 0 &&
1703
0
          psOptions->srcWin.dfXSize == poSrcDS->GetRasterXSize() &&
1704
0
          psOptions->srcWin.dfYSize == poSrcDS->GetRasterYSize() &&
1705
0
          psOptions->asGCPs.empty() && !bGotBounds && !bGotGeoTransform))
1706
0
    {
1707
0
        char **papszIter = papszMetadata;
1708
0
        while (papszIter && *papszIter)
1709
0
        {
1710
0
            if (STARTS_WITH_CI(*papszIter, "NITF_BLOCKA_"))
1711
0
            {
1712
0
                CPLFree(*papszIter);
1713
0
                memmove(papszIter, papszIter + 1,
1714
0
                        sizeof(char *) * (CSLCount(papszIter + 1) + 1));
1715
0
            }
1716
0
            else
1717
0
                papszIter++;
1718
0
        }
1719
0
    }
1720
1721
0
    {
1722
0
        char **papszIter = papszMetadata;
1723
0
        while (papszIter && *papszIter)
1724
0
        {
1725
            // Do not preserve the CACHE_PATH from the WMS driver
1726
0
            if (STARTS_WITH_CI(*papszIter, "CACHE_PATH="))
1727
0
            {
1728
0
                CPLFree(*papszIter);
1729
0
                memmove(papszIter, papszIter + 1,
1730
0
                        sizeof(char *) * (CSLCount(papszIter + 1) + 1));
1731
0
            }
1732
0
            else
1733
0
                papszIter++;
1734
0
        }
1735
0
    }
1736
1737
0
    if (CSLFetchNameValue(papszMetadata, "NODATA_VALUES") &&
1738
0
        !(bAllBandsInOrder &&
1739
0
          psOptions->nBandCount == poSrcDS->GetRasterCount()))
1740
0
    {
1741
0
        papszMetadata =
1742
0
            CSLSetNameValue(papszMetadata, "NODATA_VALUES", nullptr);
1743
0
    }
1744
1745
0
    poVDS->SetMetadata(papszMetadata);
1746
0
    CSLDestroy(papszMetadata);
1747
0
    AttachMetadata(GDALDataset::ToHandle(poVDS), psOptions->aosMetadataOptions);
1748
1749
0
    AttachDomainMetadata(GDALDataset::ToHandle(poVDS),
1750
0
                         psOptions->aosDomainMetadataOptions);
1751
1752
0
    const char *pszInterleave =
1753
0
        poSrcDS->GetMetadataItem(GDALMD_INTERLEAVE, GDAL_MDD_IMAGE_STRUCTURE);
1754
0
    if (pszInterleave)
1755
0
        poVDS->SetMetadataItem(GDALMD_INTERLEAVE, pszInterleave,
1756
0
                               GDAL_MDD_IMAGE_STRUCTURE);
1757
1758
0
    {
1759
0
        const char *pszCompression = poSrcDS->GetMetadataItem(
1760
0
            GDALMD_COMPRESSION, GDAL_MDD_IMAGE_STRUCTURE);
1761
0
        if (pszCompression)
1762
0
        {
1763
0
            poVDS->SetMetadataItem(GDALMD_COMPRESSION, pszCompression,
1764
0
                                   GDAL_MDD_IMAGE_STRUCTURE);
1765
0
        }
1766
0
    }
1767
1768
    /* ISIS3 metadata preservation */
1769
0
    CSLConstList papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
1770
0
    if (papszMD_ISIS3 != nullptr && papszMD_ISIS3[0])
1771
0
    {
1772
0
        std::string osJSON = papszMD_ISIS3[0];
1773
0
        if (!bAllBandsInOrder)
1774
0
        {
1775
0
            osJSON = EditISIS3MetadataForBandChange(
1776
0
                osJSON.c_str(), poSrcDS->GetRasterCount(), psOptions.get());
1777
0
        }
1778
0
        if (!bSpatialArrangementPreserved || bValuesChanged)
1779
0
        {
1780
0
            osJSON = EditISIS3ForMetadataChanges(
1781
0
                osJSON.c_str(), bKeepExtent, bKeepResolution, psOptions.get());
1782
0
        }
1783
0
        if (!osJSON.empty())
1784
0
        {
1785
0
            char *apszMD[] = {osJSON.data(), nullptr};
1786
0
            poVDS->SetMetadata(apszMD, "json:ISIS3");
1787
0
        }
1788
0
    }
1789
1790
    // PDS4 -> PDS4 special case
1791
0
    if (EQUAL(psOptions->osFormat.c_str(), "PDS4"))
1792
0
    {
1793
0
        CSLConstList papszMD_PDS4 = poSrcDS->GetMetadata("xml:PDS4");
1794
0
        if (papszMD_PDS4 != nullptr)
1795
0
            poVDS->SetMetadata(papszMD_PDS4, "xml:PDS4");
1796
0
    }
1797
1798
    // VICAR -> VICAR special case
1799
0
    if (EQUAL(psOptions->osFormat.c_str(), "VICAR"))
1800
0
    {
1801
0
        CSLConstList papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
1802
0
        if (papszMD_VICAR != nullptr)
1803
0
            poVDS->SetMetadata(papszMD_VICAR, "json:VICAR");
1804
0
    }
1805
1806
    // Copy XMP metadata
1807
0
    if (!psOptions->bNoXMP)
1808
0
    {
1809
0
        CSLConstList papszXMP = poSrcDS->GetMetadata("xml:XMP");
1810
0
        if (papszXMP != nullptr && *papszXMP != nullptr)
1811
0
        {
1812
0
            poVDS->SetMetadata(papszXMP, "xml:XMP");
1813
0
        }
1814
0
    }
1815
1816
    /* -------------------------------------------------------------------- */
1817
    /*      Transfer metadata that remains valid if the spatial             */
1818
    /*      arrangement of the data is unaltered.                           */
1819
    /* -------------------------------------------------------------------- */
1820
0
    if (bSpatialArrangementPreserved)
1821
0
    {
1822
0
        CSLConstList papszMD = poSrcDS->GetMetadata(GDAL_MDD_RPC);
1823
0
        if (papszMD != nullptr)
1824
0
            poVDS->SetMetadata(papszMD, GDAL_MDD_RPC);
1825
1826
0
        papszMD = poSrcDS->GetMetadata(GDAL_MDD_GEOLOCATION);
1827
0
        if (papszMD != nullptr)
1828
0
            poVDS->SetMetadata(papszMD, GDAL_MDD_GEOLOCATION);
1829
0
    }
1830
0
    else
1831
0
    {
1832
0
        CPLStringList aosMD(poSrcDSOri->GetMetadata(GDAL_MDD_RPC));
1833
0
        if (!aosMD.empty())
1834
0
        {
1835
0
            double dfSAMP_OFF =
1836
0
                CPLAtof(aosMD.FetchNameValueDef("SAMP_OFF", "0"));
1837
0
            double dfLINE_OFF =
1838
0
                CPLAtof(aosMD.FetchNameValueDef("LINE_OFF", "0"));
1839
0
            double dfSAMP_SCALE =
1840
0
                CPLAtof(aosMD.FetchNameValueDef("SAMP_SCALE", "1"));
1841
0
            double dfLINE_SCALE =
1842
0
                CPLAtof(aosMD.FetchNameValueDef("LINE_SCALE", "1"));
1843
1844
0
            dfSAMP_OFF -= srcWinOri.dfXOff;
1845
0
            dfLINE_OFF -= srcWinOri.dfYOff;
1846
1847
0
            const double df2 = srcWinOri.dfXSize;
1848
0
            const double df3 = srcWinOri.dfYSize;
1849
0
            const double dfXRatio = nOXSize / df2;
1850
0
            const double dfYRatio = nOYSize / df3;
1851
1852
            // For line offset and pixel offset, we need to convert from RPC
1853
            // pixel center registration convention to GDAL pixel top-left corner
1854
            // registration convention by adding an initial 0.5 shift, and un-apply
1855
            // it after scaling.
1856
1857
0
            dfSAMP_OFF += 0.5;
1858
0
            dfSAMP_OFF *= dfXRatio;
1859
0
            dfSAMP_OFF -= 0.5;
1860
1861
0
            dfLINE_OFF += 0.5;
1862
0
            dfLINE_OFF *= dfYRatio;
1863
0
            dfLINE_OFF -= 0.5;
1864
1865
0
            dfSAMP_SCALE *= dfXRatio;
1866
0
            dfLINE_SCALE *= dfYRatio;
1867
1868
0
            CPLString osField;
1869
0
            osField.Printf("%.15g", dfLINE_OFF);
1870
0
            aosMD.SetNameValue("LINE_OFF", osField);
1871
1872
0
            osField.Printf("%.15g", dfSAMP_OFF);
1873
0
            aosMD.SetNameValue("SAMP_OFF", osField);
1874
1875
0
            osField.Printf("%.15g", dfLINE_SCALE);
1876
0
            aosMD.SetNameValue("LINE_SCALE", osField);
1877
1878
0
            osField.Printf("%.15g", dfSAMP_SCALE);
1879
0
            aosMD.SetNameValue("SAMP_SCALE", osField);
1880
1881
0
            poVDS->SetMetadata(aosMD.List(), GDAL_MDD_RPC);
1882
0
        }
1883
0
    }
1884
1885
0
    const int nSrcBandCount = psOptions->nBandCount;
1886
1887
0
    if (psOptions->nRGBExpand != 0)
1888
0
    {
1889
0
        GDALRasterBand *poSrcBand =
1890
0
            poSrcDS->GetRasterBand(std::abs(psOptions->anBandList[0]));
1891
0
        if (psOptions->anBandList[0] < 0)
1892
0
            poSrcBand = poSrcBand->GetMaskBand();
1893
0
        GDALColorTable *poColorTable = poSrcBand->GetColorTable();
1894
0
        if (poColorTable == nullptr)
1895
0
        {
1896
0
            CPLError(CE_Failure, CPLE_AppDefined,
1897
0
                     "Error : band %d has no color table",
1898
0
                     std::abs(psOptions->anBandList[0]));
1899
0
            GDALClose(poVDS);
1900
0
            return nullptr;
1901
0
        }
1902
1903
        /* Check that the color table only contains gray levels */
1904
        /* when using -expand gray */
1905
0
        if (psOptions->nRGBExpand == 1)
1906
0
        {
1907
0
            int nColorCount = poColorTable->GetColorEntryCount();
1908
0
            for (int nColor = 0; nColor < nColorCount; nColor++)
1909
0
            {
1910
0
                const GDALColorEntry *poEntry =
1911
0
                    poColorTable->GetColorEntry(nColor);
1912
0
                if (poEntry->c1 != poEntry->c2 || poEntry->c1 != poEntry->c3)
1913
0
                {
1914
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1915
0
                             "Warning : color table contains non gray levels "
1916
0
                             "colors");
1917
0
                    break;
1918
0
                }
1919
0
            }
1920
0
        }
1921
1922
0
        if (psOptions->nBandCount == 1)
1923
0
        {
1924
0
            psOptions->nBandCount = psOptions->nRGBExpand;
1925
0
        }
1926
0
        else if (psOptions->nBandCount == 2 &&
1927
0
                 (psOptions->nRGBExpand == 3 || psOptions->nRGBExpand == 4))
1928
0
        {
1929
0
            psOptions->nBandCount = psOptions->nRGBExpand;
1930
0
        }
1931
0
        else
1932
0
        {
1933
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1934
0
                     "Error : invalid use of -expand option.");
1935
0
            GDALClose(poVDS);
1936
0
            return nullptr;
1937
0
        }
1938
0
    }
1939
1940
    // Can be set to TRUE in the band loop too
1941
0
    bool bFilterOutStatsMetadata = bValuesChanged ||
1942
0
                                   !bSpatialArrangementPreserved ||
1943
0
                                   psOptions->nRGBExpand != 0;
1944
1945
0
    if (static_cast<int>(psOptions->anColorInterp.size()) >
1946
0
        psOptions->nBandCount)
1947
0
    {
1948
0
        CPLError(CE_Warning, CPLE_AppDefined,
1949
0
                 "More bands defined in -colorinterp than output bands");
1950
0
    }
1951
1952
    /* ==================================================================== */
1953
    /*      Process all bands.                                              */
1954
    /* ==================================================================== */
1955
0
    GDALDataType eOutputType = psOptions->eOutputType;
1956
1957
0
    for (int i = 0; i < psOptions->nBandCount; i++)
1958
0
    {
1959
0
        int nComponent = 0;
1960
0
        int nSrcBand = 0;
1961
1962
0
        if (psOptions->nRGBExpand != 0)
1963
0
        {
1964
0
            if (nSrcBandCount == 2 && psOptions->nRGBExpand == 4 && i == 3)
1965
0
                nSrcBand = psOptions->anBandList[1];
1966
0
            else
1967
0
            {
1968
0
                nSrcBand = psOptions->anBandList[0];
1969
0
                nComponent = i + 1;
1970
0
            }
1971
0
        }
1972
0
        else
1973
0
        {
1974
0
            nSrcBand = psOptions->anBandList[i];
1975
0
        }
1976
1977
0
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(std::abs(nSrcBand));
1978
1979
        /* --------------------------------------------------------------------
1980
         */
1981
        /*      Select output data type to match source. */
1982
        /* --------------------------------------------------------------------
1983
         */
1984
0
        GDALRasterBand *poRealSrcBand =
1985
0
            (nSrcBand < 0) ? poSrcBand->GetMaskBand() : poSrcBand;
1986
0
        GDALDataType eBandType;
1987
0
        if (eOutputType == GDT_Unknown)
1988
0
        {
1989
0
            eBandType = poRealSrcBand->GetRasterDataType();
1990
0
            if (eBandType != GDT_UInt8 && psOptions->nRGBExpand != 0)
1991
0
            {
1992
                // Use case of https://github.com/OSGeo/gdal/issues/9402
1993
0
                if (const auto poColorTable = poRealSrcBand->GetColorTable())
1994
0
                {
1995
0
                    bool bIn0To255Range = true;
1996
0
                    const int nColorCount = poColorTable->GetColorEntryCount();
1997
0
                    for (int nColor = 0; nColor < nColorCount; nColor++)
1998
0
                    {
1999
0
                        const GDALColorEntry *poEntry =
2000
0
                            poColorTable->GetColorEntry(nColor);
2001
0
                        if (poEntry->c1 > 255 || poEntry->c2 > 255 ||
2002
0
                            poEntry->c3 > 255 || poEntry->c4 > 255)
2003
0
                        {
2004
0
                            bIn0To255Range = false;
2005
0
                            break;
2006
0
                        }
2007
0
                    }
2008
0
                    if (bIn0To255Range)
2009
0
                    {
2010
0
                        if (!psOptions->bQuiet)
2011
0
                        {
2012
0
                            CPLError(CE_Warning, CPLE_AppDefined,
2013
0
                                     "Using Byte output data type due to range "
2014
0
                                     "of values in color table");
2015
0
                        }
2016
0
                        eBandType = GDT_UInt8;
2017
0
                    }
2018
0
                }
2019
0
                eOutputType = eBandType;
2020
0
            }
2021
0
        }
2022
0
        else
2023
0
        {
2024
0
            eBandType = eOutputType;
2025
2026
            // Check that we can copy existing statistics
2027
0
            GDALDataType eSrcBandType = poRealSrcBand->GetRasterDataType();
2028
0
            const char *pszMin =
2029
0
                poRealSrcBand->GetMetadataItem("STATISTICS_MINIMUM");
2030
0
            const char *pszMax =
2031
0
                poRealSrcBand->GetMetadataItem("STATISTICS_MAXIMUM");
2032
0
            if (!bFilterOutStatsMetadata && eBandType != eSrcBandType &&
2033
0
                pszMin != nullptr && pszMax != nullptr)
2034
0
            {
2035
0
                const bool bSrcIsInteger =
2036
0
                    CPL_TO_BOOL(GDALDataTypeIsInteger(eSrcBandType)) &&
2037
0
                    !CPL_TO_BOOL(GDALDataTypeIsComplex(eSrcBandType));
2038
0
                const bool bDstIsInteger =
2039
0
                    CPL_TO_BOOL(GDALDataTypeIsInteger(eBandType)) &&
2040
0
                    !CPL_TO_BOOL(GDALDataTypeIsComplex(eBandType));
2041
0
                if (bSrcIsInteger && bDstIsInteger)
2042
0
                {
2043
0
                    std::int64_t nDstMin = 0;
2044
0
                    std::uint64_t nDstMax = 0;
2045
0
                    switch (eBandType)
2046
0
                    {
2047
0
                        case GDT_UInt8:
2048
0
                            nDstMin = std::numeric_limits<std::uint8_t>::min();
2049
0
                            nDstMax = std::numeric_limits<std::uint8_t>::max();
2050
0
                            break;
2051
0
                        case GDT_Int8:
2052
0
                            nDstMin = std::numeric_limits<std::int8_t>::min();
2053
0
                            nDstMax = std::numeric_limits<std::int8_t>::max();
2054
0
                            break;
2055
0
                        case GDT_UInt16:
2056
0
                            nDstMin = std::numeric_limits<std::uint16_t>::min();
2057
0
                            nDstMax = std::numeric_limits<std::uint16_t>::max();
2058
0
                            break;
2059
0
                        case GDT_Int16:
2060
0
                            nDstMin = std::numeric_limits<std::int16_t>::min();
2061
0
                            nDstMax = std::numeric_limits<std::int16_t>::max();
2062
0
                            break;
2063
0
                        case GDT_UInt32:
2064
0
                            nDstMin = std::numeric_limits<std::uint32_t>::min();
2065
0
                            nDstMax = std::numeric_limits<std::uint32_t>::max();
2066
0
                            break;
2067
0
                        case GDT_Int32:
2068
0
                            nDstMin = std::numeric_limits<std::int32_t>::min();
2069
0
                            nDstMax = std::numeric_limits<std::int32_t>::max();
2070
0
                            break;
2071
0
                        case GDT_UInt64:
2072
0
                            nDstMin = std::numeric_limits<std::uint64_t>::min();
2073
0
                            nDstMax = std::numeric_limits<std::uint64_t>::max();
2074
0
                            break;
2075
0
                        case GDT_Int64:
2076
0
                            nDstMin = std::numeric_limits<std::int64_t>::min();
2077
0
                            nDstMax = std::numeric_limits<std::int64_t>::max();
2078
0
                            break;
2079
0
                        default:
2080
0
                            CPLAssert(false);
2081
0
                            break;
2082
0
                    }
2083
2084
0
                    try
2085
0
                    {
2086
0
                        const auto nMin = std::stoll(pszMin);
2087
0
                        const auto nMax = std::stoull(pszMax);
2088
0
                        if (nMin < nDstMin || nMax > nDstMax)
2089
0
                            bFilterOutStatsMetadata = true;
2090
0
                    }
2091
0
                    catch (const std::exception &)
2092
0
                    {
2093
0
                    }
2094
0
                }
2095
                // Float64 is large enough to hold all integer <= 32 bit or
2096
                // float32 values there might be other OK cases, but ere on safe
2097
                // side for now
2098
0
                else if (!((bSrcIsInteger || eSrcBandType == GDT_Float32) &&
2099
0
                           eBandType == GDT_Float64))
2100
0
                {
2101
0
                    bFilterOutStatsMetadata = true;
2102
0
                }
2103
0
            }
2104
0
        }
2105
2106
        /* --------------------------------------------------------------------
2107
         */
2108
        /*      Create this band. */
2109
        /* --------------------------------------------------------------------
2110
         */
2111
0
        CPLStringList aosAddBandOptions;
2112
0
        int nSrcBlockXSize, nSrcBlockYSize;
2113
0
        poSrcBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
2114
0
        if (bKeepResolution &&
2115
0
            (fmod(psOptions->srcWin.dfXOff, nSrcBlockXSize)) == 0 &&
2116
0
            (fmod(psOptions->srcWin.dfYOff, nSrcBlockYSize)) == 0)
2117
0
        {
2118
0
            aosAddBandOptions.SetNameValue("BLOCKXSIZE",
2119
0
                                           CPLSPrintf("%d", nSrcBlockXSize));
2120
0
            aosAddBandOptions.SetNameValue("BLOCKYSIZE",
2121
0
                                           CPLSPrintf("%d", nSrcBlockYSize));
2122
0
        }
2123
0
        const char *pszBlockXSize =
2124
0
            psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE");
2125
0
        if (pszBlockXSize)
2126
0
            aosAddBandOptions.SetNameValue("BLOCKXSIZE", pszBlockXSize);
2127
0
        const char *pszBlockYSize =
2128
0
            psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE");
2129
0
        if (pszBlockYSize)
2130
0
            aosAddBandOptions.SetNameValue("BLOCKYSIZE", pszBlockYSize);
2131
0
        poVDS->AddBand(eBandType, aosAddBandOptions.List());
2132
0
        VRTSourcedRasterBand *poVRTBand =
2133
0
            static_cast<VRTSourcedRasterBand *>(poVDS->GetRasterBand(i + 1));
2134
2135
0
        if (nSrcBand < 0)
2136
0
        {
2137
0
            poVRTBand->AddMaskBandSource(
2138
0
                poSrcBand, psOptions->srcWin.dfXOff, psOptions->srcWin.dfYOff,
2139
0
                psOptions->srcWin.dfXSize, psOptions->srcWin.dfYSize,
2140
0
                dstWin.dfXOff, dstWin.dfYOff, dstWin.dfXSize, dstWin.dfYSize);
2141
2142
0
            poVRTBand->SetColorInterpretation(GCI_AlphaBand);
2143
2144
            // Color interpretation override
2145
0
            if (!psOptions->anColorInterp.empty())
2146
0
            {
2147
0
                if (i < static_cast<int>(psOptions->anColorInterp.size()) &&
2148
0
                    psOptions->anColorInterp[i] >= 0)
2149
0
                {
2150
0
                    poVRTBand->SetColorInterpretation(
2151
0
                        static_cast<GDALColorInterp>(
2152
0
                            psOptions->anColorInterp[i]));
2153
0
                }
2154
0
            }
2155
2156
0
            continue;
2157
0
        }
2158
2159
        // Preserve NBITS if no option change values
2160
0
        const char *pszNBits =
2161
0
            poSrcBand->GetMetadataItem(GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
2162
0
        if (pszNBits && psOptions->nRGBExpand == 0 &&
2163
0
            psOptions->asScaleParams.empty() && !psOptions->bUnscale &&
2164
0
            psOptions->eOutputType == GDT_Unknown &&
2165
0
            psOptions->osResampling.empty())
2166
0
        {
2167
0
            poVRTBand->SetMetadataItem(GDALMD_NBITS, pszNBits,
2168
0
                                       GDAL_MDD_IMAGE_STRUCTURE);
2169
0
        }
2170
2171
        // Preserve PIXELTYPE if no option change values
2172
0
        if (poSrcBand->GetRasterDataType() == GDT_UInt8 &&
2173
0
            psOptions->nRGBExpand == 0 && psOptions->asScaleParams.empty() &&
2174
0
            !psOptions->bUnscale && psOptions->eOutputType == GDT_Unknown &&
2175
0
            psOptions->osResampling.empty())
2176
0
        {
2177
0
            poSrcBand->EnablePixelTypeSignedByteWarning(false);
2178
0
            const char *pszPixelType = poSrcBand->GetMetadataItem(
2179
0
                "PIXELTYPE", GDAL_MDD_IMAGE_STRUCTURE);
2180
0
            poSrcBand->EnablePixelTypeSignedByteWarning(true);
2181
0
            if (pszPixelType)
2182
0
            {
2183
0
                poVRTBand->SetMetadataItem("PIXELTYPE", pszPixelType,
2184
0
                                           GDAL_MDD_IMAGE_STRUCTURE);
2185
0
            }
2186
0
        }
2187
2188
0
        const char *pszCompression = poSrcBand->GetMetadataItem(
2189
0
            GDALMD_COMPRESSION, GDAL_MDD_IMAGE_STRUCTURE);
2190
0
        if (pszCompression)
2191
0
        {
2192
0
            poVRTBand->SetMetadataItem(GDALMD_COMPRESSION, pszCompression,
2193
0
                                       GDAL_MDD_IMAGE_STRUCTURE);
2194
0
        }
2195
2196
        /* --------------------------------------------------------------------
2197
         */
2198
        /*      Do we need to collect scaling information? */
2199
        /* --------------------------------------------------------------------
2200
         */
2201
0
        double dfScale = 1.0;
2202
0
        double dfOffset = 0.0;
2203
0
        bool bScale = false;
2204
0
        double dfScaleSrcMin = std::numeric_limits<double>::quiet_NaN();
2205
0
        double dfScaleSrcMax = std::numeric_limits<double>::quiet_NaN();
2206
0
        double dfScaleDstMin = std::numeric_limits<double>::quiet_NaN();
2207
0
        double dfScaleDstMax = std::numeric_limits<double>::quiet_NaN();
2208
0
        bool bExponentScaling = false;
2209
0
        double dfExponent = 0.0;
2210
2211
0
        if (i < static_cast<int>(psOptions->asScaleParams.size()) &&
2212
0
            psOptions->asScaleParams[i].bScale)
2213
0
        {
2214
0
            bScale = psOptions->asScaleParams[i].bScale;
2215
0
            dfScaleSrcMin = psOptions->asScaleParams[i].dfScaleSrcMin;
2216
0
            dfScaleSrcMax = psOptions->asScaleParams[i].dfScaleSrcMax;
2217
0
            dfScaleDstMin = psOptions->asScaleParams[i].dfScaleDstMin;
2218
0
            dfScaleDstMax = psOptions->asScaleParams[i].dfScaleDstMax;
2219
0
        }
2220
0
        else if (psOptions->asScaleParams.size() == 1 &&
2221
0
                 !psOptions->bHasUsedExplicitScaleBand)
2222
0
        {
2223
0
            bScale = psOptions->asScaleParams[0].bScale;
2224
0
            dfScaleSrcMin = psOptions->asScaleParams[0].dfScaleSrcMin;
2225
0
            dfScaleSrcMax = psOptions->asScaleParams[0].dfScaleSrcMax;
2226
0
            dfScaleDstMin = psOptions->asScaleParams[0].dfScaleDstMin;
2227
0
            dfScaleDstMax = psOptions->asScaleParams[0].dfScaleDstMax;
2228
0
        }
2229
2230
0
        if (i < static_cast<int>(psOptions->adfExponent.size()) &&
2231
0
            psOptions->adfExponent[i] != 0.0)
2232
0
        {
2233
0
            bExponentScaling = TRUE;
2234
0
            dfExponent = psOptions->adfExponent[i];
2235
0
        }
2236
0
        else if (psOptions->adfExponent.size() == 1 &&
2237
0
                 !psOptions->bHasUsedExplicitExponentBand)
2238
0
        {
2239
0
            bExponentScaling = TRUE;
2240
0
            dfExponent = psOptions->adfExponent[0];
2241
0
        }
2242
2243
0
        if (bExponentScaling && !bScale)
2244
0
        {
2245
0
            CPLError(CE_Failure, CPLE_IllegalArg,
2246
0
                     "For band %d, -scale should be specified when -exponent "
2247
0
                     "is specified.",
2248
0
                     i + 1);
2249
0
            if (pbUsageError)
2250
0
                *pbUsageError = TRUE;
2251
0
            delete poVDS;
2252
0
            poSrcDS->Release();
2253
0
            return nullptr;
2254
0
        }
2255
2256
0
        if (bScale && std::isnan(dfScaleSrcMin))
2257
0
        {
2258
0
            double adfCMinMax[2] = {};
2259
0
            GDALComputeRasterMinMax(poSrcBand, TRUE, adfCMinMax);
2260
0
            dfScaleSrcMin = adfCMinMax[0];
2261
0
            dfScaleSrcMax = adfCMinMax[1];
2262
0
        }
2263
2264
0
        if (bScale)
2265
0
        {
2266
            /* To avoid a divide by zero */
2267
0
            if (dfScaleSrcMax == dfScaleSrcMin)
2268
0
                dfScaleSrcMax += 0.1;
2269
2270
            // Can still occur for very big values
2271
0
            if (dfScaleSrcMax == dfScaleSrcMin)
2272
0
            {
2273
0
                CPLError(CE_Failure, CPLE_AppDefined,
2274
0
                         "-scale cannot be applied due to source "
2275
0
                         "minimum and maximum being equal");
2276
0
                delete poVDS;
2277
0
                poSrcDS->Release();
2278
0
                return nullptr;
2279
0
            }
2280
2281
0
            if (std::isnan(dfScaleDstMin))
2282
0
            {
2283
0
                switch (poVRTBand->GetRasterDataType())
2284
0
                {
2285
0
                    case GDT_UInt8:
2286
0
                        dfScaleDstMin = std::numeric_limits<uint8_t>::lowest();
2287
0
                        dfScaleDstMax = std::numeric_limits<uint8_t>::max();
2288
0
                        break;
2289
0
                    case GDT_Int8:
2290
0
                        dfScaleDstMin = std::numeric_limits<int8_t>::lowest();
2291
0
                        dfScaleDstMax = std::numeric_limits<int8_t>::max();
2292
0
                        break;
2293
0
                    case GDT_UInt16:
2294
0
                        dfScaleDstMin = std::numeric_limits<uint16_t>::lowest();
2295
0
                        dfScaleDstMax = std::numeric_limits<uint16_t>::max();
2296
0
                        break;
2297
0
                    case GDT_Int16:
2298
0
                    case GDT_CInt16:
2299
0
                        dfScaleDstMin = std::numeric_limits<int16_t>::lowest();
2300
0
                        dfScaleDstMax = std::numeric_limits<int16_t>::max();
2301
0
                        break;
2302
0
                    case GDT_UInt32:
2303
0
                        dfScaleDstMin = std::numeric_limits<uint32_t>::lowest();
2304
0
                        dfScaleDstMax = std::numeric_limits<uint32_t>::max();
2305
0
                        break;
2306
0
                    case GDT_Int32:
2307
0
                    case GDT_CInt32:
2308
0
                        dfScaleDstMin = std::numeric_limits<int32_t>::lowest();
2309
0
                        dfScaleDstMax = std::numeric_limits<int32_t>::max();
2310
0
                        break;
2311
0
                    case GDT_UInt64:
2312
0
                        dfScaleDstMin = static_cast<double>(
2313
0
                            std::numeric_limits<uint64_t>::lowest());
2314
0
                        dfScaleDstMax = static_cast<double>(
2315
0
                            std::numeric_limits<uint64_t>::max() - 2048);
2316
0
                        break;
2317
0
                    case GDT_Int64:
2318
0
                        dfScaleDstMin = static_cast<double>(
2319
0
                            std::numeric_limits<int64_t>::lowest() + 1024);
2320
0
                        dfScaleDstMax = static_cast<double>(
2321
0
                            std::numeric_limits<int64_t>::max() - 2048);
2322
0
                        break;
2323
0
                    case GDT_Float16:
2324
0
                    case GDT_Float32:
2325
0
                    case GDT_Float64:
2326
0
                    case GDT_CFloat16:
2327
0
                    case GDT_CFloat32:
2328
0
                    case GDT_CFloat64:
2329
0
                    case GDT_Unknown:
2330
0
                    case GDT_TypeCount:
2331
0
                        dfScaleDstMin = 0;
2332
0
                        dfScaleDstMax = 1;
2333
0
                        break;
2334
0
                }
2335
0
            }
2336
2337
0
            if (!bExponentScaling)
2338
0
            {
2339
0
                dfScale = (dfScaleDstMax - dfScaleDstMin) /
2340
0
                          (dfScaleSrcMax - dfScaleSrcMin);
2341
0
                dfOffset = -1 * dfScaleSrcMin * dfScale + dfScaleDstMin;
2342
0
            }
2343
0
        }
2344
2345
0
        if (psOptions->bUnscale)
2346
0
        {
2347
0
            dfScale = poSrcBand->GetScale();
2348
0
            dfOffset = poSrcBand->GetOffset();
2349
0
        }
2350
2351
        /* --------------------------------------------------------------------
2352
         */
2353
        /*      Create a simple or complex data source depending on the */
2354
        /*      translation type required. */
2355
        /* --------------------------------------------------------------------
2356
         */
2357
0
        std::unique_ptr<VRTSimpleSource> poSimpleSource;
2358
0
        if (psOptions->bUnscale || bScale ||
2359
0
            (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand))
2360
0
        {
2361
0
            auto poComplexSource = std::make_unique<VRTComplexSource>();
2362
2363
            /* --------------------------------------------------------------------
2364
             */
2365
            /*      Set complex parameters. */
2366
            /* --------------------------------------------------------------------
2367
             */
2368
2369
0
            if (dfOffset != 0.0 || dfScale != 1.0)
2370
0
            {
2371
0
                poComplexSource->SetLinearScaling(dfOffset, dfScale);
2372
0
            }
2373
0
            else if (bExponentScaling)
2374
0
            {
2375
0
                poComplexSource->SetPowerScaling(
2376
0
                    dfExponent, dfScaleSrcMin, dfScaleSrcMax, dfScaleDstMin,
2377
0
                    dfScaleDstMax, !psOptions->bNoClip);
2378
0
            }
2379
2380
0
            poComplexSource->SetColorTableComponent(nComponent);
2381
2382
0
            int bSuccess;
2383
0
            double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
2384
0
            if (bSuccess)
2385
0
            {
2386
0
                poComplexSource->SetNoDataValue(dfNoData);
2387
0
            }
2388
2389
0
            poSimpleSource = std::move(poComplexSource);
2390
0
        }
2391
0
        else
2392
0
        {
2393
0
            poSimpleSource = std::make_unique<VRTSimpleSource>();
2394
0
        }
2395
2396
0
        poSimpleSource->SetResampling(psOptions->osResampling.empty()
2397
0
                                          ? nullptr
2398
0
                                          : psOptions->osResampling.c_str());
2399
0
        poVRTBand->ConfigureSource(
2400
0
            poSimpleSource.get(), poSrcBand, FALSE, psOptions->srcWin.dfXOff,
2401
0
            psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
2402
0
            psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
2403
0
            dstWin.dfXSize, dstWin.dfYSize);
2404
2405
0
        poVRTBand->AddSource(std::move(poSimpleSource));
2406
2407
        /* --------------------------------------------------------------------
2408
         */
2409
        /*      In case of color table translate, we only set the color */
2410
        /*      interpretation other info copied by CopyBandInfo are */
2411
        /*      not relevant in RGB expansion. */
2412
        /* --------------------------------------------------------------------
2413
         */
2414
0
        if (psOptions->nRGBExpand == 1)
2415
0
        {
2416
0
            poVRTBand->SetColorInterpretation(GCI_GrayIndex);
2417
0
        }
2418
0
        else if (psOptions->nRGBExpand != 0 && i < psOptions->nRGBExpand)
2419
0
        {
2420
0
            poVRTBand->SetColorInterpretation(
2421
0
                static_cast<GDALColorInterp>(GCI_RedBand + i));
2422
0
        }
2423
2424
        /* --------------------------------------------------------------------
2425
         */
2426
        /*      copy over some other information of interest. */
2427
        /* --------------------------------------------------------------------
2428
         */
2429
0
        else
2430
0
        {
2431
0
            CopyBandInfo(poSrcBand, poVRTBand,
2432
0
                         !psOptions->bStats && !bFilterOutStatsMetadata,
2433
0
                         !psOptions->bUnscale && !psOptions->bSetScale &&
2434
0
                             !psOptions->bSetOffset,
2435
0
                         !psOptions->bSetNoData && !psOptions->bUnsetNoData,
2436
0
                         !psOptions->bNoRAT, psOptions.get());
2437
0
            if (psOptions->asScaleParams.empty() &&
2438
0
                psOptions->adfExponent.empty() &&
2439
0
                EQUAL(psOptions->osFormat.c_str(), "GRIB"))
2440
0
            {
2441
0
                CSLConstList papszMD_GRIB = poSrcBand->GetMetadata("GRIB");
2442
0
                if (papszMD_GRIB != nullptr)
2443
0
                    poVRTBand->SetMetadata(papszMD_GRIB, "GRIB");
2444
0
            }
2445
0
        }
2446
2447
        // Color interpretation override
2448
0
        if (!psOptions->anColorInterp.empty())
2449
0
        {
2450
0
            if (i < static_cast<int>(psOptions->anColorInterp.size()) &&
2451
0
                psOptions->anColorInterp[i] >= 0)
2452
0
            {
2453
0
                poVRTBand->SetColorInterpretation(
2454
0
                    static_cast<GDALColorInterp>(psOptions->anColorInterp[i]));
2455
0
            }
2456
0
        }
2457
2458
        /* --------------------------------------------------------------------
2459
         */
2460
        /*      Set a forcible nodata value? */
2461
        /* --------------------------------------------------------------------
2462
         */
2463
0
        if (psOptions->bSetNoData)
2464
0
        {
2465
0
            const char *pszPixelType =
2466
0
                psOptions->aosCreateOptions.FetchNameValue("PIXELTYPE");
2467
0
            if (pszPixelType == nullptr &&
2468
0
                poVRTBand->GetRasterDataType() == GDT_UInt8)
2469
0
            {
2470
0
                poVRTBand->EnablePixelTypeSignedByteWarning(false);
2471
0
                pszPixelType = poVRTBand->GetMetadataItem(
2472
0
                    "PIXELTYPE", GDAL_MDD_IMAGE_STRUCTURE);
2473
0
                poVRTBand->EnablePixelTypeSignedByteWarning(true);
2474
0
            }
2475
2476
0
            bool bCannotBeExactlyRepresented = false;
2477
2478
0
            if (pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE"))
2479
0
            {
2480
0
                char *endptr = nullptr;
2481
0
                const double dfVal =
2482
0
                    CPLStrtod(psOptions->osNoData.c_str(), &endptr);
2483
0
                if (endptr == psOptions->osNoData.c_str() +
2484
0
                                  psOptions->osNoData.size() &&
2485
0
                    dfVal >= -128.0 && dfVal <= 127.0 &&
2486
0
                    static_cast<int8_t>(dfVal) == dfVal)
2487
0
                {
2488
0
                    poVRTBand->SetNoDataValue(dfVal);
2489
0
                }
2490
0
                else
2491
0
                {
2492
0
                    bCannotBeExactlyRepresented = true;
2493
0
                }
2494
0
            }
2495
0
            else
2496
0
            {
2497
0
                poVRTBand->SetNoDataValueAsString(psOptions->osNoData.c_str(),
2498
0
                                                  &bCannotBeExactlyRepresented);
2499
0
            }
2500
0
            if (bCannotBeExactlyRepresented)
2501
0
            {
2502
0
                CPLError(CE_Warning, CPLE_AppDefined,
2503
0
                         "Nodata value was not set to output band, "
2504
0
                         "as it cannot be represented on its data type.");
2505
0
            }
2506
0
        }
2507
2508
0
        if (psOptions->bSetScale)
2509
0
            poVRTBand->SetScale(psOptions->dfScale);
2510
2511
0
        if (psOptions->bSetOffset)
2512
0
            poVRTBand->SetOffset(psOptions->dfOffset);
2513
2514
0
        if (psOptions->eMaskMode == MASK_AUTO &&
2515
0
            (poSrcDS->GetRasterBand(1)->GetMaskFlags() & GMF_PER_DATASET) ==
2516
0
                0 &&
2517
0
            (poSrcBand->GetMaskFlags() & (GMF_ALL_VALID | GMF_NODATA)) == 0)
2518
0
        {
2519
0
            if (poVRTBand->CreateMaskBand(poSrcBand->GetMaskFlags()) == CE_None)
2520
0
            {
2521
0
                VRTSourcedRasterBand *hMaskVRTBand =
2522
0
                    cpl::down_cast<VRTSourcedRasterBand *>(
2523
0
                        poVRTBand->GetMaskBand());
2524
0
                hMaskVRTBand->AddMaskBandSource(
2525
0
                    poSrcBand, psOptions->srcWin.dfXOff,
2526
0
                    psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
2527
0
                    psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
2528
0
                    dstWin.dfXSize, dstWin.dfYSize);
2529
0
            }
2530
0
        }
2531
0
    }
2532
2533
0
    if (psOptions->eMaskMode == MASK_USER)
2534
0
    {
2535
0
        GDALRasterBand *poSrcBand =
2536
0
            poSrcDS->GetRasterBand(std::abs(psOptions->nMaskBand));
2537
0
        if (poSrcBand && poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
2538
0
        {
2539
0
            VRTSourcedRasterBand *hMaskVRTBand =
2540
0
                static_cast<VRTSourcedRasterBand *>(GDALGetMaskBand(
2541
0
                    GDALGetRasterBand(static_cast<GDALDataset *>(poVDS), 1)));
2542
0
            if (psOptions->nMaskBand > 0)
2543
0
                hMaskVRTBand->AddSimpleSource(
2544
0
                    poSrcBand, psOptions->srcWin.dfXOff,
2545
0
                    psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
2546
0
                    psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
2547
0
                    dstWin.dfXSize, dstWin.dfYSize);
2548
0
            else
2549
0
                hMaskVRTBand->AddMaskBandSource(
2550
0
                    poSrcBand, psOptions->srcWin.dfXOff,
2551
0
                    psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
2552
0
                    psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
2553
0
                    dstWin.dfXSize, dstWin.dfYSize);
2554
0
        }
2555
0
    }
2556
0
    else if (psOptions->eMaskMode == MASK_AUTO && nSrcBandCount > 0 &&
2557
0
             poSrcDS->GetRasterBand(1)->GetMaskFlags() == GMF_PER_DATASET)
2558
0
    {
2559
0
        if (poVDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
2560
0
        {
2561
0
            VRTSourcedRasterBand *hMaskVRTBand =
2562
0
                static_cast<VRTSourcedRasterBand *>(GDALGetMaskBand(
2563
0
                    GDALGetRasterBand(static_cast<GDALDataset *>(poVDS), 1)));
2564
0
            hMaskVRTBand->AddMaskBandSource(
2565
0
                poSrcDS->GetRasterBand(1), psOptions->srcWin.dfXOff,
2566
0
                psOptions->srcWin.dfYOff, psOptions->srcWin.dfXSize,
2567
0
                psOptions->srcWin.dfYSize, dstWin.dfXOff, dstWin.dfYOff,
2568
0
                dstWin.dfXSize, dstWin.dfYSize);
2569
0
        }
2570
0
    }
2571
2572
    /* -------------------------------------------------------------------- */
2573
    /*      Compute stats if required.                                      */
2574
    /* -------------------------------------------------------------------- */
2575
0
    if (psOptions->bStats && EQUAL(psOptions->osFormat.c_str(), "COG"))
2576
0
    {
2577
0
        psOptions->aosCreateOptions.SetNameValue("STATISTICS", "YES");
2578
0
    }
2579
0
    else if (psOptions->bStats)
2580
0
    {
2581
0
        for (int i = 0; i < poVDS->GetRasterCount(); i++)
2582
0
        {
2583
0
            double dfMin, dfMax, dfMean, dfStdDev;
2584
0
            poVDS->GetRasterBand(i + 1)->ComputeStatistics(
2585
0
                psOptions->bApproxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
2586
0
                GDALDummyProgress, nullptr, nullptr);
2587
0
        }
2588
0
    }
2589
2590
    /* -------------------------------------------------------------------- */
2591
    /*      Write to the output file using CopyCreate().                    */
2592
    /* -------------------------------------------------------------------- */
2593
0
    if (EQUAL(psOptions->osFormat.c_str(), "VRT") &&
2594
0
        (psOptions->aosCreateOptions.empty() ||
2595
0
         (psOptions->aosCreateOptions.size() == 1 &&
2596
0
          psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE")) ||
2597
0
         (psOptions->aosCreateOptions.size() == 1 &&
2598
0
          psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE")) ||
2599
0
         (psOptions->aosCreateOptions.size() == 2 &&
2600
0
          psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE") &&
2601
0
          psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE"))))
2602
0
    {
2603
0
        poVDS->SetDescription(pszDest);
2604
0
        hOutDS = GDALDataset::ToHandle(poVDS);
2605
0
        if (!EQUAL(pszDest, ""))
2606
0
        {
2607
0
            hOutDS = GDALTranslateFlush(hOutDS);
2608
0
        }
2609
0
    }
2610
0
    else
2611
0
    {
2612
0
        hOutDS = GDALCreateCopy(
2613
0
            hDriver, pszDest, GDALDataset::ToHandle(poVDS), psOptions->bStrict,
2614
0
            psOptions->aosCreateOptions.List(), psOptions->pfnProgress,
2615
0
            psOptions->pProgressData);
2616
0
        hOutDS = GDALTranslateFlush(hOutDS);
2617
2618
0
        GDALClose(poVDS);
2619
0
    }
2620
2621
0
    poSrcDS->Release();
2622
2623
0
    return hOutDS;
2624
0
}
2625
2626
/************************************************************************/
2627
/*                           AttachMetadata()                           */
2628
/************************************************************************/
2629
2630
static void AttachMetadata(GDALDatasetH hDS,
2631
                           const CPLStringList &aosMetadataOptions)
2632
2633
0
{
2634
0
    for (const auto &[pszKey, pszValue] :
2635
0
         cpl::IterateNameValue(aosMetadataOptions))
2636
0
    {
2637
0
        GDALSetMetadataItem(hDS, pszKey, pszValue, nullptr);
2638
0
    }
2639
0
}
2640
2641
/************************************************************************/
2642
/*                        AttachDomainMetadata()                        */
2643
/************************************************************************/
2644
2645
static void AttachDomainMetadata(GDALDatasetH hDS,
2646
                                 const CPLStringList &aosDomainMetadataOptions)
2647
2648
0
{
2649
0
    for (const char *pszStr : aosDomainMetadataOptions)
2650
0
    {
2651
2652
0
        char *pszKey = nullptr;
2653
0
        char *pszDomain = nullptr;
2654
2655
        // parse the DOMAIN:KEY=value, Remainder is KEY=value
2656
0
        const char *pszRemainder =
2657
0
            CPLParseNameValueSep(pszStr, &pszDomain, ':');
2658
2659
0
        if (pszDomain && pszRemainder)
2660
0
        {
2661
2662
0
            const char *pszValue =
2663
0
                CPLParseNameValueSep(pszRemainder, &pszKey, '=');
2664
0
            if (pszKey && pszValue)
2665
0
            {
2666
0
                GDALSetMetadataItem(hDS, pszKey, pszValue, pszDomain);
2667
0
            }
2668
0
        }
2669
0
        CPLFree(pszKey);
2670
2671
0
        CPLFree(pszDomain);
2672
0
    }
2673
0
}
2674
2675
/************************************************************************/
2676
/*                            CopyBandInfo()                            */
2677
/************************************************************************/
2678
2679
/* A bit of a clone of VRTRasterBand::CopyCommonInfoFrom(), but we need */
2680
/* more and more custom behavior in the context of gdal_translate ... */
2681
2682
static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
2683
                         int bCanCopyStatsMetadata, int bCopyScale,
2684
                         int bCopyNoData, bool bCopyRAT,
2685
                         const GDALTranslateOptions * /*psOptions*/)
2686
2687
0
{
2688
2689
0
    if (bCanCopyStatsMetadata)
2690
0
    {
2691
0
        poDstBand->SetMetadata(poSrcBand->GetMetadata());
2692
0
        if (bCopyRAT)
2693
0
        {
2694
0
            poDstBand->SetDefaultRAT(poSrcBand->GetDefaultRAT());
2695
0
        }
2696
0
    }
2697
0
    else
2698
0
    {
2699
0
        CSLConstList papszMetadata = poSrcBand->GetMetadata();
2700
0
        char **papszMetadataNew = nullptr;
2701
0
        for (int i = 0; papszMetadata != nullptr && papszMetadata[i] != nullptr;
2702
0
             i++)
2703
0
        {
2704
0
            if (!STARTS_WITH(papszMetadata[i], "STATISTICS_"))
2705
0
                papszMetadataNew =
2706
0
                    CSLAddString(papszMetadataNew, papszMetadata[i]);
2707
0
        }
2708
0
        poDstBand->SetMetadata(papszMetadataNew);
2709
0
        CSLDestroy(papszMetadataNew);
2710
2711
        // we need to strip histogram data from the source RAT
2712
0
        if (poSrcBand->GetDefaultRAT() && bCopyRAT)
2713
0
        {
2714
0
            GDALRasterAttributeTable *poNewRAT =
2715
0
                poSrcBand->GetDefaultRAT()->Clone();
2716
2717
            // strip histogram data (as defined by the source RAT)
2718
0
            poNewRAT->RemoveStatistics();
2719
0
            if (poNewRAT->GetColumnCount())
2720
0
            {
2721
0
                poDstBand->SetDefaultRAT(poNewRAT);
2722
0
            }
2723
            // since SetDefaultRAT copies the RAT data we need to delete our
2724
            // original
2725
0
            delete poNewRAT;
2726
0
        }
2727
0
    }
2728
2729
0
    poDstBand->SetColorTable(poSrcBand->GetColorTable());
2730
0
    poDstBand->SetColorInterpretation(poSrcBand->GetColorInterpretation());
2731
0
    if (strlen(poSrcBand->GetDescription()) > 0)
2732
0
        poDstBand->SetDescription(poSrcBand->GetDescription());
2733
2734
0
    if (bCopyNoData)
2735
0
    {
2736
0
        int bSuccess = FALSE;
2737
0
        CPL_IGNORE_RET_VAL(poSrcBand->GetNoDataValue(&bSuccess));
2738
0
        if (bSuccess)
2739
0
        {
2740
0
            bool bCannotBeExactlyRepresented = false;
2741
0
            if (!GDALCopyNoDataValue(poDstBand, poSrcBand,
2742
0
                                     &bCannotBeExactlyRepresented) &&
2743
0
                bCannotBeExactlyRepresented)
2744
0
            {
2745
0
                CPLError(CE_Warning, CPLE_AppDefined,
2746
0
                         "Source nodata value was not copied to output band, "
2747
0
                         "as it cannot be represented on its data type.");
2748
0
            }
2749
0
        }
2750
0
    }
2751
2752
0
    if (bCopyScale)
2753
0
    {
2754
0
        poDstBand->SetOffset(poSrcBand->GetOffset());
2755
0
        poDstBand->SetScale(poSrcBand->GetScale());
2756
0
    }
2757
2758
0
    poDstBand->SetCategoryNames(poSrcBand->GetCategoryNames());
2759
2760
    // Copy unit only if the range of pixel values is not modified
2761
0
    if (bCanCopyStatsMetadata && bCopyScale &&
2762
0
        !EQUAL(poSrcBand->GetUnitType(), ""))
2763
0
        poDstBand->SetUnitType(poSrcBand->GetUnitType());
2764
0
}
2765
2766
/************************************************************************/
2767
/*                           GetColorInterp()                           */
2768
/************************************************************************/
2769
2770
static int GetColorInterp(const char *pszStr)
2771
0
{
2772
0
    if (EQUAL(pszStr, "undefined"))
2773
0
        return GCI_Undefined;
2774
0
    const int eInterp = GDALGetColorInterpretationByName(pszStr);
2775
0
    if (eInterp != GCI_Undefined)
2776
0
        return eInterp;
2777
0
    CPLError(CE_Warning, CPLE_NotSupported,
2778
0
             "Unsupported color interpretation: %s", pszStr);
2779
0
    return -1;
2780
0
}
2781
2782
/************************************************************************/
2783
/*                   GDALTranslateOptionsGetParser()                    */
2784
/************************************************************************/
2785
2786
static std::unique_ptr<GDALArgumentParser>
2787
GDALTranslateOptionsGetParser(GDALTranslateOptions *psOptions,
2788
                              GDALTranslateOptionsForBinary *psOptionsForBinary)
2789
0
{
2790
0
    auto argParser = std::make_unique<GDALArgumentParser>(
2791
0
        "gdal_translate", /* bForBinary=*/psOptionsForBinary != nullptr);
2792
2793
0
    argParser->add_description(
2794
0
        _("Convert raster data between different formats, with potential "
2795
0
          "subsetting, resampling, and rescaling pixels in the process."));
2796
2797
0
    argParser->add_epilog(_("For more details, consult "
2798
0
                            "https://gdal.org/programs/gdal_translate.html"));
2799
2800
0
    argParser->add_output_type_argument(psOptions->eOutputType);
2801
2802
0
    argParser->add_argument("-if")
2803
0
        .append()
2804
0
        .metavar("<format>")
2805
0
        .action(
2806
0
            [psOptionsForBinary](const std::string &s)
2807
0
            {
2808
0
                if (psOptionsForBinary)
2809
0
                {
2810
0
                    if (GDALGetDriverByName(s.c_str()) == nullptr)
2811
0
                    {
2812
0
                        CPLError(CE_Warning, CPLE_AppDefined,
2813
0
                                 "%s is not a recognized driver", s.c_str());
2814
0
                    }
2815
0
                    psOptionsForBinary->aosAllowedInputDrivers.AddString(
2816
0
                        s.c_str());
2817
0
                }
2818
0
            })
2819
0
        .help(_("Format/driver name(s) to try when opening the input file."));
2820
2821
0
    argParser->add_output_format_argument(psOptions->osFormat);
2822
2823
0
    argParser->add_quiet_argument(&(psOptions->bQuiet));
2824
2825
0
    argParser->add_argument("-b")
2826
0
        .append()
2827
0
        .metavar("<band>")
2828
0
        .action(
2829
0
            [psOptions](const std::string &s)
2830
0
            {
2831
0
                const char *pszBand = s.c_str();
2832
0
                bool bMask = false;
2833
0
                if (EQUAL(pszBand, "mask"))
2834
0
                    pszBand = "mask,1";
2835
0
                if (STARTS_WITH_CI(pszBand, "mask,"))
2836
0
                {
2837
0
                    bMask = true;
2838
0
                    pszBand += 5;
2839
                    /* If we use the source mask band as a regular band */
2840
                    /* don't create a target mask band by default */
2841
0
                    if (!psOptions->bParsedMaskArgument)
2842
0
                        psOptions->eMaskMode = MASK_DISABLED;
2843
0
                }
2844
0
                const int nBand = atoi(pszBand);
2845
0
                if (nBand < 1)
2846
0
                {
2847
0
                    throw std::invalid_argument(CPLSPrintf(
2848
0
                        "Unrecognizable band number (%s).", s.c_str()));
2849
0
                }
2850
2851
0
                psOptions->nBandCount++;
2852
0
                psOptions->anBandList.emplace_back(nBand * (bMask ? -1 : 1));
2853
0
            })
2854
0
        .help(_("Select input band(s)"));
2855
2856
0
    argParser->add_argument("-mask")
2857
0
        .metavar("<mask>")
2858
0
        .action(
2859
0
            [psOptions](const std::string &s)
2860
0
            {
2861
0
                psOptions->bParsedMaskArgument = true;
2862
0
                const char *pszBand = s.c_str();
2863
0
                if (EQUAL(pszBand, "none"))
2864
0
                {
2865
0
                    psOptions->eMaskMode = MASK_DISABLED;
2866
0
                }
2867
0
                else if (EQUAL(pszBand, "auto"))
2868
0
                {
2869
0
                    psOptions->eMaskMode = MASK_AUTO;
2870
0
                }
2871
0
                else
2872
0
                {
2873
0
                    bool bMask = false;
2874
2875
0
                    if (EQUAL(pszBand, "mask"))
2876
0
                        pszBand = "mask,1";
2877
0
                    if (STARTS_WITH_CI(pszBand, "mask,"))
2878
0
                    {
2879
0
                        bMask = true;
2880
0
                        pszBand += 5;
2881
0
                    }
2882
0
                    const int nBand = atoi(pszBand);
2883
0
                    if (nBand < 1)
2884
0
                    {
2885
0
                        throw std::invalid_argument(CPLSPrintf(
2886
0
                            "Unrecognizable band number (%s).", s.c_str()));
2887
0
                    }
2888
2889
0
                    psOptions->eMaskMode = MASK_USER;
2890
0
                    psOptions->nMaskBand = nBand;
2891
0
                    if (bMask)
2892
0
                        psOptions->nMaskBand *= -1;
2893
0
                }
2894
0
            })
2895
0
        .help(_("Select an input band to create output dataset mask band"));
2896
2897
0
    argParser->add_argument("-expand")
2898
0
        .metavar("gray|rgb|rgba")
2899
0
        .action(
2900
0
            [psOptions](const std::string &s)
2901
0
            {
2902
0
                if (EQUAL(s.c_str(), "gray"))
2903
0
                    psOptions->nRGBExpand = 1;
2904
0
                else if (EQUAL(s.c_str(), "rgb"))
2905
0
                    psOptions->nRGBExpand = 3;
2906
0
                else if (EQUAL(s.c_str(), "rgba"))
2907
0
                    psOptions->nRGBExpand = 4;
2908
0
                else
2909
0
                {
2910
0
                    throw std::invalid_argument(CPLSPrintf(
2911
0
                        "Value %s unsupported. Only gray, rgb or rgba are "
2912
0
                        "supported.",
2913
0
                        s.c_str()));
2914
0
                }
2915
0
            })
2916
0
        .help(_("To expose a dataset with 1 band with a color table as a "
2917
0
                "dataset with 3 (RGB) or 4 (RGBA) bands."));
2918
2919
0
    {
2920
0
        auto &group = argParser->add_mutually_exclusive_group();
2921
0
        group.add_argument("-strict")
2922
0
            .store_into(psOptions->bStrict)
2923
0
            .help(_("Enable strict mode"));
2924
2925
0
        group.add_argument("-not_strict")
2926
0
            .flag()
2927
0
            .action([psOptions](const std::string &)
2928
0
                    { psOptions->bStrict = false; })
2929
0
            .help(_("Disable strict mode"));
2930
0
    }
2931
2932
0
    argParser->add_argument("-outsize")
2933
0
        .metavar("<xsize[%]|0> <ysize[%]|0>")
2934
0
        .nargs(2)
2935
0
        .help(_("Set the size of the output file."));
2936
2937
0
    argParser->add_argument("-tr")
2938
0
        .metavar("<xres> <yres>")
2939
0
        .nargs(2)
2940
0
        .scan<'g', double>()
2941
0
        .help(_("Set target resolution."));
2942
2943
0
    argParser->add_argument("-ovr")
2944
0
        .metavar("<level>|AUTO|AUTO-<n>|NONE")
2945
0
        .action(
2946
0
            [psOptions](const std::string &s)
2947
0
            {
2948
0
                const char *pszOvLevel = s.c_str();
2949
0
                if (EQUAL(pszOvLevel, "AUTO"))
2950
0
                    psOptions->nOvLevel = OVR_LEVEL_AUTO;
2951
0
                else if (STARTS_WITH_CI(pszOvLevel, "AUTO-"))
2952
0
                    psOptions->nOvLevel =
2953
0
                        OVR_LEVEL_AUTO - atoi(pszOvLevel + strlen("AUTO-"));
2954
0
                else if (EQUAL(pszOvLevel, "NONE"))
2955
0
                    psOptions->nOvLevel = OVR_LEVEL_NONE;
2956
0
                else if (CPLGetValueType(pszOvLevel) == CPL_VALUE_INTEGER)
2957
0
                    psOptions->nOvLevel = atoi(pszOvLevel);
2958
0
                else
2959
0
                {
2960
0
                    throw std::invalid_argument(CPLSPrintf(
2961
0
                        "Invalid value '%s' for -ovr option", pszOvLevel));
2962
0
                }
2963
0
            })
2964
0
        .help(_("Specify which overview level of source file must be used"));
2965
2966
0
    if (psOptionsForBinary)
2967
0
    {
2968
0
        argParser->add_argument("-sds")
2969
0
            .store_into(psOptionsForBinary->bCopySubDatasets)
2970
0
            .help(_("Copy subdatasets"));
2971
0
    }
2972
2973
0
    argParser->add_argument("-r")
2974
0
        .metavar("nearest,bilinear,cubic,cubicspline,lanczos,average,mode")
2975
0
        .store_into(psOptions->osResampling)
2976
0
        .help(_("Resampling algorithm."));
2977
2978
0
    {
2979
0
        auto &group = argParser->add_mutually_exclusive_group();
2980
0
        group.add_argument("-scale")
2981
0
            .metavar("[<src_min> <src_max> [<dst_min> <dst_max>]]")
2982
            //.nargs(0, 4)
2983
0
            .append()
2984
0
            .scan<'g', double>()
2985
0
            .help(_("Rescale the input pixels values from the range src_min to "
2986
0
                    "src_max to the range dst_min to dst_max."));
2987
2988
0
        group.add_argument("-scale_X")
2989
0
            .metavar("[<src_min> <src_max> [<dst_min> <dst_max>]]")
2990
            //.nargs(0, 4)
2991
0
            .append()
2992
0
            .scan<'g', double>()
2993
0
            .help(_("Rescale the input pixels values for band X."));
2994
2995
0
        group.add_argument("-unscale")
2996
0
            .store_into(psOptions->bUnscale)
2997
0
            .help(_("Apply the scale/offset metadata for the bands to convert "
2998
0
                    "scaled values to unscaled values."));
2999
0
    }
3000
3001
0
    {
3002
0
        auto &group = argParser->add_mutually_exclusive_group();
3003
0
        group.add_argument("-exponent")
3004
0
            .metavar("<value>")
3005
0
            .scan<'g', double>()
3006
0
            .help(_(
3007
0
                "Exponent to apply non-linear scaling with a power function"));
3008
3009
0
        group.add_argument("-exponent_X")
3010
0
            .append()
3011
0
            .metavar("<value>")
3012
0
            .scan<'g', double>()
3013
0
            .help(
3014
0
                _("Exponent to apply non-linear scaling with a power function, "
3015
0
                  "for band X"));
3016
0
    }
3017
3018
0
    argParser->add_argument("-srcwin")
3019
0
        .metavar("<xoff> <yoff> <xsize> <ysize>")
3020
0
        .nargs(4)
3021
0
        .scan<'g', double>()
3022
0
        .help(_("Selects a subwindow from the source image based on pixel/line "
3023
0
                "location."));
3024
3025
0
    argParser->add_argument("-projwin")
3026
0
        .metavar("<ulx> <uly> <lrx> <lry>")
3027
0
        .nargs(4)
3028
0
        .scan<'g', double>()
3029
0
        .help(_("Selects a subwindow from the source image based on "
3030
0
                "georeferenced coordinates."));
3031
3032
0
    argParser->add_argument("-projwin_srs")
3033
0
        .metavar("<srs_def>")
3034
0
        .store_into(psOptions->osProjSRS)
3035
0
        .help(_("Specifies the SRS in which to interpret the coordinates given "
3036
0
                "with -projwin."));
3037
3038
0
    argParser->add_argument("-epo")
3039
0
        .flag()
3040
0
        .action(
3041
0
            [psOptions](const std::string &)
3042
0
            {
3043
0
                psOptions->bErrorOnPartiallyOutside = true;
3044
0
                psOptions->bErrorOnCompletelyOutside = true;
3045
0
            })
3046
0
        .help(_("Error when Partially Outside."));
3047
3048
0
    argParser->add_argument("-eco")
3049
0
        .store_into(psOptions->bErrorOnCompletelyOutside)
3050
0
        .help(_("Error when Completely Outside."));
3051
3052
0
    argParser->add_argument("-a_srs")
3053
0
        .metavar("<srs_def>")
3054
0
        .store_into(psOptions->osOutputSRS)
3055
0
        .help(_("Override the projection for the output file."));
3056
3057
0
    argParser->add_argument("-a_coord_epoch")
3058
0
        .metavar("<epoch>")
3059
0
        .store_into(psOptions->dfOutputCoordinateEpoch)
3060
0
        .help(_("Assign a coordinate epoch."));
3061
3062
0
    argParser->add_argument("-a_ullr")
3063
0
        .metavar("<ulx> <uly> <lrx> <lry>")
3064
0
        .nargs(4)
3065
0
        .scan<'g', double>()
3066
0
        .help(
3067
0
            _("Assign/override the georeferenced bounds of the output file."));
3068
3069
0
    argParser->add_argument("-a_nodata")
3070
0
        .metavar("<value>|none")
3071
0
        .help(_("Assign a specified nodata value to output bands."));
3072
3073
0
    argParser->add_argument("-a_gt")
3074
0
        .metavar("<gt(0)> <gt(1)> <gt(2)> <gt(3)> <gt(4)> <gt(5)>")
3075
0
        .nargs(6)
3076
0
        .scan<'g', double>()
3077
0
        .help(_("Assign/override the geotransform of the output file."));
3078
3079
0
    argParser->add_argument("-a_scale")
3080
0
        .metavar("<value>")
3081
0
        .store_into(psOptions->dfScale)
3082
0
        .help(_("Set band scaling value."));
3083
3084
0
    argParser->add_argument("-a_offset")
3085
0
        .metavar("<value>")
3086
0
        .store_into(psOptions->dfOffset)
3087
0
        .help(_("Set band offset value."));
3088
3089
0
    argParser->add_argument("-nogcp")
3090
0
        .store_into(psOptions->bNoGCP)
3091
0
        .help(_("Do not copy the GCPs in the source dataset to the output "
3092
0
                "dataset."));
3093
3094
0
    argParser->add_argument("-gcp")
3095
0
        .metavar("<pixel> <line> <easting> <northing> [<elevation>]")
3096
0
        .nargs(4, 5)
3097
0
        .append()
3098
0
        .scan<'g', double>()
3099
0
        .help(
3100
0
            _("Add the indicated ground control point to the output dataset."));
3101
3102
0
    argParser->add_argument("-colorinterp")
3103
0
        .metavar("{red|green|blue|alpha|gray|undefined|pan|coastal|rededge|nir|"
3104
0
                 "swir|mwir|lwir|...},...")
3105
0
        .action(
3106
0
            [psOptions](const std::string &s)
3107
0
            {
3108
0
                CPLStringList aosList(CSLTokenizeString2(s.c_str(), ",", 0));
3109
0
                psOptions->anColorInterp.resize(aosList.size());
3110
0
                for (int j = 0; j < aosList.size(); j++)
3111
0
                {
3112
0
                    psOptions->anColorInterp[j] = GetColorInterp(aosList[j]);
3113
0
                }
3114
0
            })
3115
0
        .help(_("Override the color interpretation of all specified bands."));
3116
3117
0
    argParser->add_argument("-colorinterp_X")
3118
0
        .append()
3119
0
        .metavar("{red|green|blue|alpha|gray|undefined|pan|coastal|rededge|nir|"
3120
0
                 "swir|mwir|lwir|...}")
3121
0
        .help(_("Override the color interpretation of band X."));
3122
3123
0
    {
3124
0
        auto &group = argParser->add_mutually_exclusive_group();
3125
0
        group.add_argument("-stats")
3126
0
            .flag()
3127
0
            .action(
3128
0
                [psOptions](const std::string &)
3129
0
                {
3130
0
                    psOptions->bStats = true;
3131
0
                    psOptions->bApproxStats = false;
3132
0
                })
3133
0
            .help(_("Force (re)computation of statistics."));
3134
3135
0
        group.add_argument("-approx_stats")
3136
0
            .flag()
3137
0
            .action(
3138
0
                [psOptions](const std::string &)
3139
0
                {
3140
0
                    psOptions->bStats = true;
3141
0
                    psOptions->bApproxStats = true;
3142
0
                })
3143
0
            .help(_("Force (re)computation of approximate statistics."));
3144
0
    }
3145
3146
0
    argParser->add_argument("-norat")
3147
0
        .store_into(psOptions->bNoRAT)
3148
0
        .help(_("Do not copy source RAT into destination dataset."));
3149
3150
0
    argParser->add_argument("-noxmp")
3151
0
        .store_into(psOptions->bNoXMP)
3152
0
        .help(_("Do not copy the XMP metadata into destination dataset."));
3153
3154
0
    argParser->add_creation_options_argument(psOptions->aosCreateOptions);
3155
3156
0
    argParser->add_metadata_item_options_argument(
3157
0
        psOptions->aosMetadataOptions);
3158
3159
0
    argParser->add_argument("-dmo")
3160
0
        .metavar("<DOMAIN>:<KEY>=<VALUE>")
3161
0
        .append()
3162
0
        .action([psOptions](const std::string &s)
3163
0
                { psOptions->aosDomainMetadataOptions.AddString(s.c_str()); })
3164
0
        .help(_("Passes a metadata key and value in specified domain to set on "
3165
0
                "the output dataset if possible."));
3166
3167
0
    argParser->add_open_options_argument(
3168
0
        psOptionsForBinary ? &(psOptionsForBinary->aosOpenOptions) : nullptr);
3169
3170
    // Undocumented option used by gdal_translate_fuzzer
3171
0
    argParser->add_argument("-limit_outsize")
3172
0
        .hidden()
3173
0
        .store_into(psOptions->nLimitOutSize);
3174
3175
    // Undocumented option used by gdal raster convert
3176
0
    argParser->add_argument("--no-overwrite")
3177
0
        .store_into(psOptions->bNoOverwrite)
3178
0
        .hidden();
3179
3180
    // Undocumented option used by gdal raster scale
3181
0
    argParser->add_argument("--no-clip")
3182
0
        .store_into(psOptions->bNoClip)
3183
0
        .hidden();
3184
3185
    // Undocumented option used by gdal raster clip
3186
0
    argParser->add_argument("--no-warn-about-outside-window")
3187
0
        .store_into(psOptions->bNoWarnAboutOutsideWindow)
3188
0
        .hidden();
3189
3190
0
    if (psOptionsForBinary)
3191
0
    {
3192
0
        argParser->add_argument("input_file")
3193
0
            .metavar("<input_file>")
3194
0
            .store_into(psOptionsForBinary->osSource)
3195
0
            .help(_("Input file."));
3196
3197
0
        argParser->add_argument("output_file")
3198
0
            .metavar("<output_file>")
3199
0
            .store_into(psOptionsForBinary->osDest)
3200
0
            .help(_("Output file."));
3201
0
    }
3202
3203
0
    return argParser;
3204
0
}
3205
3206
/************************************************************************/
3207
/*                    GDALTranslateGetParserUsage()                     */
3208
/************************************************************************/
3209
3210
std::string GDALTranslateGetParserUsage()
3211
0
{
3212
0
    try
3213
0
    {
3214
0
        GDALTranslateOptions sOptions;
3215
0
        GDALTranslateOptionsForBinary sOptionsForBinary;
3216
0
        auto argParser =
3217
0
            GDALTranslateOptionsGetParser(&sOptions, &sOptionsForBinary);
3218
0
        return argParser->usage();
3219
0
    }
3220
0
    catch (const std::exception &err)
3221
0
    {
3222
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
3223
0
                 err.what());
3224
0
        return std::string();
3225
0
    }
3226
0
}
3227
3228
/************************************************************************/
3229
/*                      GDALTranslateOptionsNew()                       */
3230
/************************************************************************/
3231
3232
/**
3233
 * Allocates a GDALTranslateOptions struct.
3234
 *
3235
 * @param papszArgv NULL terminated list of options (potentially including
3236
 * filename and open options too), or NULL. The accepted options are the ones of
3237
 * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
3238
 * @param psOptionsForBinary (output) may be NULL (and should generally be
3239
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
3240
 *                           GDALTranslateOptionsForBinaryNew() prior to this
3241
 * function. Will be filled with potentially present filename, open options,...
3242
 * @return pointer to the allocated GDALTranslateOptions struct. Must be freed
3243
 * with GDALTranslateOptionsFree().
3244
 *
3245
 * @since GDAL 2.1
3246
 */
3247
3248
GDALTranslateOptions *
3249
GDALTranslateOptionsNew(char **papszArgv,
3250
                        GDALTranslateOptionsForBinary *psOptionsForBinary)
3251
0
{
3252
0
    auto psOptions = std::make_unique<GDALTranslateOptions>();
3253
3254
0
    psOptions->aosArgs.Assign(CSLDuplicate(papszArgv), true);
3255
3256
    /* -------------------------------------------------------------------- */
3257
    /*      Pre-processing for custom syntax that ArgumentParser does not   */
3258
    /*      support.                                                        */
3259
    /* -------------------------------------------------------------------- */
3260
3261
0
    CPLStringList aosArgv;
3262
0
    const int argc = CSLCount(papszArgv);
3263
0
    for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
3264
0
         i++)
3265
0
    {
3266
0
        if (i + 4 < argc && EQUAL(papszArgv[i], "-gcp"))
3267
0
        {
3268
            /* -gcp pixel line easting northing [elev] */
3269
0
            psOptions->asGCPs.resize(psOptions->asGCPs.size() + 1);
3270
0
            psOptions->asGCPs.back().Pixel() = CPLAtofM(papszArgv[++i]);
3271
0
            psOptions->asGCPs.back().Line() = CPLAtofM(papszArgv[++i]);
3272
0
            psOptions->asGCPs.back().X() = CPLAtofM(papszArgv[++i]);
3273
0
            psOptions->asGCPs.back().Y() = CPLAtofM(papszArgv[++i]);
3274
3275
0
            char *endptr = nullptr;
3276
0
            if (papszArgv[i + 1] != nullptr &&
3277
0
                (CPLStrtod(papszArgv[i + 1], &endptr) != 0.0 ||
3278
0
                 papszArgv[i + 1][0] == '0'))
3279
0
            {
3280
                /* Check that last argument is really a number and not a
3281
                 * filename */
3282
                /* looking like a number (see ticket #863) */
3283
0
                if (endptr && *endptr == 0)
3284
0
                    psOptions->asGCPs.back().Z() = CPLAtofM(papszArgv[++i]);
3285
0
            }
3286
3287
            /* should set id and info? */
3288
0
        }
3289
3290
0
        else if (EQUAL(papszArgv[i], "-scale") ||
3291
0
                 STARTS_WITH_CI(papszArgv[i], "-scale_"))
3292
0
        {
3293
0
            int nIndex = 0;
3294
0
            if (STARTS_WITH_CI(papszArgv[i], "-scale_"))
3295
0
            {
3296
0
                if (!psOptions->bHasUsedExplicitScaleBand &&
3297
0
                    !psOptions->asScaleParams.empty())
3298
0
                {
3299
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3300
0
                             "Cannot mix -scale and -scale_XX syntax");
3301
0
                    return nullptr;
3302
0
                }
3303
0
                psOptions->bHasUsedExplicitScaleBand = true;
3304
0
                nIndex = atoi(papszArgv[i] + 7);
3305
0
                if (nIndex <= 0 || nIndex > 65535)
3306
0
                {
3307
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3308
0
                             "Invalid parameter name: %s", papszArgv[i]);
3309
0
                    return nullptr;
3310
0
                }
3311
0
                nIndex--;
3312
0
            }
3313
0
            else
3314
0
            {
3315
0
                if (psOptions->bHasUsedExplicitScaleBand)
3316
0
                {
3317
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3318
0
                             "Cannot mix -scale and -scale_XX syntax");
3319
0
                    return nullptr;
3320
0
                }
3321
0
                nIndex = static_cast<int>(psOptions->asScaleParams.size());
3322
0
            }
3323
3324
0
            if (nIndex >= static_cast<int>(psOptions->asScaleParams.size()))
3325
0
            {
3326
0
                psOptions->asScaleParams.resize(nIndex + 1);
3327
0
            }
3328
0
            psOptions->asScaleParams[nIndex].bScale = true;
3329
0
            bool bScanForDst = false;
3330
0
            if (i < argc - 2 && EQUAL(papszArgv[i + 1], "NaN") &&
3331
0
                EQUAL(papszArgv[i + 2], "NaN"))
3332
0
            {
3333
0
                bScanForDst = true;
3334
0
                i += 2;
3335
0
            }
3336
0
            else if (i < argc - 2 && ArgIsNumeric(papszArgv[i + 1]))
3337
0
            {
3338
0
                if (!ArgIsNumeric(papszArgv[i + 2]))
3339
0
                {
3340
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
3341
0
                             "Value of -scale must be numeric");
3342
0
                    return nullptr;
3343
0
                }
3344
0
                psOptions->asScaleParams[nIndex].dfScaleSrcMin =
3345
0
                    CPLAtofM(papszArgv[i + 1]);
3346
0
                psOptions->asScaleParams[nIndex].dfScaleSrcMax =
3347
0
                    CPLAtofM(papszArgv[i + 2]);
3348
0
                bScanForDst = true;
3349
0
                i += 2;
3350
0
            }
3351
0
            if (i < argc - 2 && bScanForDst && ArgIsNumeric(papszArgv[i + 1]))
3352
0
            {
3353
0
                if (!ArgIsNumeric(papszArgv[i + 2]))
3354
0
                {
3355
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
3356
0
                             "Value of -scale must be numeric");
3357
0
                    return nullptr;
3358
0
                }
3359
0
                psOptions->asScaleParams[nIndex].dfScaleDstMin =
3360
0
                    CPLAtofM(papszArgv[i + 1]);
3361
0
                psOptions->asScaleParams[nIndex].dfScaleDstMax =
3362
0
                    CPLAtofM(papszArgv[i + 2]);
3363
0
                i += 2;
3364
0
            }
3365
0
        }
3366
3367
0
        else if ((EQUAL(papszArgv[i], "-exponent") ||
3368
0
                  STARTS_WITH_CI(papszArgv[i], "-exponent_")) &&
3369
0
                 papszArgv[i + 1])
3370
0
        {
3371
0
            int nIndex = 0;
3372
0
            if (STARTS_WITH_CI(papszArgv[i], "-exponent_"))
3373
0
            {
3374
0
                if (!psOptions->bHasUsedExplicitExponentBand &&
3375
0
                    !psOptions->adfExponent.empty())
3376
0
                {
3377
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3378
0
                             "Cannot mix -exponent and -exponent_XX syntax");
3379
0
                    return nullptr;
3380
0
                }
3381
0
                psOptions->bHasUsedExplicitExponentBand = true;
3382
0
                nIndex = atoi(papszArgv[i] + 10);
3383
0
                if (nIndex <= 0 || nIndex > 65535)
3384
0
                {
3385
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3386
0
                             "Invalid parameter name: %s", papszArgv[i]);
3387
0
                    return nullptr;
3388
0
                }
3389
0
                nIndex--;
3390
0
            }
3391
0
            else
3392
0
            {
3393
0
                if (psOptions->bHasUsedExplicitExponentBand)
3394
0
                {
3395
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3396
0
                             "Cannot mix -exponent and -exponent_XX syntax");
3397
0
                    return nullptr;
3398
0
                }
3399
0
                nIndex = static_cast<int>(psOptions->adfExponent.size());
3400
0
            }
3401
3402
0
            if (nIndex >= static_cast<int>(psOptions->adfExponent.size()))
3403
0
            {
3404
0
                psOptions->adfExponent.resize(nIndex + 1);
3405
0
            }
3406
0
            double dfExponent = CPLAtofM(papszArgv[++i]);
3407
0
            psOptions->adfExponent[nIndex] = dfExponent;
3408
0
        }
3409
3410
0
        else if (STARTS_WITH_CI(papszArgv[i], "-colorinterp_") &&
3411
0
                 papszArgv[i + 1])
3412
0
        {
3413
0
            int nIndex = atoi(papszArgv[i] + strlen("-colorinterp_"));
3414
0
            if (nIndex <= 0 || nIndex > 65535)
3415
0
            {
3416
0
                CPLError(CE_Failure, CPLE_NotSupported,
3417
0
                         "Invalid parameter name: %s", papszArgv[i]);
3418
0
                return nullptr;
3419
0
            }
3420
0
            nIndex--;
3421
3422
0
            if (nIndex >= static_cast<int>(psOptions->anColorInterp.size()))
3423
0
            {
3424
0
                psOptions->anColorInterp.resize(nIndex + 1, -1);
3425
0
            }
3426
0
            ++i;
3427
0
            psOptions->anColorInterp[nIndex] = GetColorInterp(papszArgv[i]);
3428
0
        }
3429
3430
        // argparser will be confused if the value of a string argument
3431
        // starts with a negative sign.
3432
0
        else if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
3433
0
        {
3434
0
            ++i;
3435
0
            const char *s = papszArgv[i];
3436
0
            if (EQUAL(s, "none") || EQUAL(s, "null"))
3437
0
            {
3438
0
                psOptions->bUnsetNoData = true;
3439
0
            }
3440
0
            else
3441
0
            {
3442
0
                psOptions->bSetNoData = true;
3443
0
                psOptions->osNoData = s;
3444
0
            }
3445
0
        }
3446
3447
0
        else
3448
0
        {
3449
0
            aosArgv.AddString(papszArgv[i]);
3450
0
        }
3451
0
    }
3452
3453
0
    try
3454
0
    {
3455
3456
0
        auto argParser =
3457
0
            GDALTranslateOptionsGetParser(psOptions.get(), psOptionsForBinary);
3458
3459
0
        argParser->parse_args_without_binary_name(aosArgv.List());
3460
3461
0
        psOptions->bSetScale = argParser->is_used("-a_scale");
3462
0
        psOptions->bSetOffset = argParser->is_used("-a_offset");
3463
3464
0
        if (auto adfULLR = argParser->present<std::vector<double>>("-a_ullr"))
3465
0
        {
3466
0
            CPLAssert(psOptions->adfULLR.size() == adfULLR->size());
3467
0
            for (size_t i = 0; i < adfULLR->size(); ++i)
3468
0
                psOptions->adfULLR[i] = (*adfULLR)[i];
3469
0
        }
3470
3471
0
        if (auto adfGT = argParser->present<std::vector<double>>("-a_gt"))
3472
0
        {
3473
0
            CPLAssert(adfGT->size() == 6);
3474
0
            for (size_t i = 0; i < adfGT->size(); ++i)
3475
0
                psOptions->gt[i] = (*adfGT)[i];
3476
0
        }
3477
3478
0
        bool bOutsizeExplicitlySet = false;
3479
0
        if (auto aosOutSize =
3480
0
                argParser->present<std::vector<std::string>>("-outsize"))
3481
0
        {
3482
0
            if ((*aosOutSize)[0].back() == '%')
3483
0
                psOptions->dfOXSizePct = CPLAtofM((*aosOutSize)[0].c_str());
3484
0
            else
3485
0
                psOptions->nOXSizePixel = atoi((*aosOutSize)[0].c_str());
3486
3487
0
            if ((*aosOutSize)[1].back() == '%')
3488
0
                psOptions->dfOYSizePct = CPLAtofM((*aosOutSize)[1].c_str());
3489
0
            else
3490
0
                psOptions->nOYSizePixel = atoi((*aosOutSize)[1].c_str());
3491
0
            bOutsizeExplicitlySet = true;
3492
0
        }
3493
3494
0
        if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
3495
0
        {
3496
0
            psOptions->dfXRes = (*adfTargetRes)[0];
3497
0
            psOptions->dfYRes = fabs((*adfTargetRes)[1]);
3498
0
            if (psOptions->dfXRes == 0 || psOptions->dfYRes == 0)
3499
0
            {
3500
0
                CPLError(CE_Failure, CPLE_IllegalArg,
3501
0
                         "Wrong value for -tr parameters.");
3502
0
                return nullptr;
3503
0
            }
3504
0
        }
3505
3506
0
        if (auto adfSrcWin = argParser->present<std::vector<double>>("-srcwin"))
3507
0
        {
3508
0
            psOptions->srcWin.dfXOff = (*adfSrcWin)[0];
3509
0
            psOptions->srcWin.dfYOff = (*adfSrcWin)[1];
3510
0
            psOptions->srcWin.dfXSize = (*adfSrcWin)[2];
3511
0
            psOptions->srcWin.dfYSize = (*adfSrcWin)[3];
3512
0
        }
3513
3514
0
        if (auto adfProjWin =
3515
0
                argParser->present<std::vector<double>>("-projwin"))
3516
0
        {
3517
0
            psOptions->dfULX = (*adfProjWin)[0];
3518
0
            psOptions->dfULY = (*adfProjWin)[1];
3519
0
            psOptions->dfLRX = (*adfProjWin)[2];
3520
0
            psOptions->dfLRY = (*adfProjWin)[3];
3521
0
        }
3522
3523
0
        if (!psOptions->asGCPs.empty() && psOptions->bNoGCP)
3524
0
        {
3525
0
            CPLError(CE_Failure, CPLE_IllegalArg,
3526
0
                     "-nogcp and -gcp cannot be used as the same time");
3527
0
            return nullptr;
3528
0
        }
3529
3530
0
        if (bOutsizeExplicitlySet && psOptions->nOXSizePixel == 0 &&
3531
0
            psOptions->dfOXSizePct == 0.0 && psOptions->nOYSizePixel == 0 &&
3532
0
            psOptions->dfOYSizePct == 0.0)
3533
0
        {
3534
0
            CPLError(CE_Failure, CPLE_NotSupported, "-outsize %d %d invalid.",
3535
0
                     psOptions->nOXSizePixel, psOptions->nOYSizePixel);
3536
0
            return nullptr;
3537
0
        }
3538
3539
0
        if (!psOptions->asScaleParams.empty() && psOptions->bUnscale)
3540
0
        {
3541
0
            CPLError(CE_Failure, CPLE_IllegalArg,
3542
0
                     "-scale and -unscale cannot be used as the same time");
3543
0
            return nullptr;
3544
0
        }
3545
3546
0
        if (psOptionsForBinary)
3547
0
        {
3548
0
            psOptionsForBinary->bQuiet = psOptions->bQuiet;
3549
0
            psOptionsForBinary->aosCreateOptions = psOptions->aosCreateOptions;
3550
0
            if (!psOptions->osFormat.empty())
3551
0
                psOptionsForBinary->osFormat = psOptions->osFormat;
3552
0
        }
3553
3554
0
        return psOptions.release();
3555
0
    }
3556
0
    catch (const std::exception &err)
3557
0
    {
3558
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
3559
0
        return nullptr;
3560
0
    }
3561
0
}
3562
3563
/************************************************************************/
3564
/*                      GDALTranslateOptionsFree()                      */
3565
/************************************************************************/
3566
3567
/**
3568
 * Frees the GDALTranslateOptions struct.
3569
 *
3570
 * @param psOptions the options struct for GDALTranslate().
3571
 *
3572
 * @since GDAL 2.1
3573
 */
3574
3575
void GDALTranslateOptionsFree(GDALTranslateOptions *psOptions)
3576
0
{
3577
0
    delete psOptions;
3578
0
}
3579
3580
/************************************************************************/
3581
/*                  GDALTranslateOptionsSetProgress()                   */
3582
/************************************************************************/
3583
3584
/**
3585
 * Set a progress function.
3586
 *
3587
 * @param psOptions the options struct for GDALTranslate().
3588
 * @param pfnProgress the progress callback.
3589
 * @param pProgressData the user data for the progress callback.
3590
 *
3591
 * @since GDAL 2.1
3592
 */
3593
3594
void GDALTranslateOptionsSetProgress(GDALTranslateOptions *psOptions,
3595
                                     GDALProgressFunc pfnProgress,
3596
                                     void *pProgressData)
3597
0
{
3598
0
    psOptions->pfnProgress = pfnProgress;
3599
0
    psOptions->pProgressData = pProgressData;
3600
0
    if (pfnProgress == GDALTermProgress)
3601
0
        psOptions->bQuiet = false;
3602
0
}