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