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