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