Coverage Report

Created: 2026-09-14 06:50

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