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