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