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