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