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