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