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