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