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