Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster create" subcommand
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_raster_create.h"
14 :
15 : #include "cpl_conv.h"
16 : #include "gdal_priv.h"
17 : #include "gdal_utils.h"
18 : #include "ogr_spatialref.h"
19 :
20 : //! @cond Doxygen_Suppress
21 :
22 : #ifndef _
23 : #define _(x) (x)
24 : #endif
25 :
26 : /************************************************************************/
27 : /* GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm() */
28 : /************************************************************************/
29 :
30 193 : GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm(
31 193 : bool standaloneStep) noexcept
32 : : GDALRasterPipelineStepAlgorithm(
33 : NAME, DESCRIPTION, HELP_URL,
34 579 : ConstructorOptions()
35 193 : .SetStandaloneStep(standaloneStep)
36 193 : .SetAddDefaultArguments(false)
37 193 : .SetAutoOpenInputDatasets(true)
38 386 : .SetInputDatasetHelpMsg("Template raster dataset")
39 386 : .SetInputDatasetAlias("like")
40 386 : .SetInputDatasetMetaVar("TEMPLATE-DATASET")
41 193 : .SetInputDatasetRequired(false)
42 193 : .SetInputDatasetPositional(false)
43 579 : .SetInputDatasetMaxCount(1))
44 : {
45 193 : AddRasterInputArgs(false, false);
46 193 : if (standaloneStep)
47 : {
48 153 : AddProgressArg();
49 153 : AddRasterOutputArgs(false);
50 : }
51 :
52 180 : auto checkResSizeInput = [this](const std::vector<std::string> &values,
53 : const std::string &arg_name,
54 2 : bool requiresInt)
55 : {
56 537 : for (const auto &s : values)
57 : {
58 359 : char *endptr = nullptr;
59 359 : const double val = CPLStrtod(s.c_str(), &endptr);
60 359 : bool ok = false;
61 359 : if (endptr == s.c_str() + s.size())
62 : {
63 295 : if (val >= 0 && val <= INT_MAX &&
64 293 : (!requiresInt || static_cast<int>(val) == val))
65 : {
66 293 : ok = true;
67 : }
68 : }
69 64 : else if (endptr &&
70 64 : ((endptr[0] == ' ' && endptr[1] == '%' &&
71 0 : endptr + 2 == s.c_str() + s.size()) ||
72 128 : (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
73 : {
74 64 : if (val >= 0)
75 : {
76 64 : ok = true;
77 : }
78 : }
79 359 : if (!ok)
80 : {
81 2 : ReportError(CE_Failure, CPLE_IllegalArg,
82 : "Invalid %s value: %s'", arg_name.c_str(),
83 : s.c_str());
84 2 : return false;
85 : }
86 : }
87 178 : return true;
88 193 : };
89 :
90 : AddArg("resolution", 0, _("Target resolution (in destination CRS units)"),
91 386 : &m_resolution_str)
92 193 : .SetMinCount(2)
93 193 : .SetMaxCount(2)
94 193 : .SetMinValueExcluded(0)
95 193 : .SetRepeatedArgAllowed(false)
96 193 : .SetDisplayHintAboutRepetition(false)
97 386 : .SetMetaVar("<xres[%]>,<yres[%]>")
98 386 : .SetMutualExclusionGroup("resolution-size")
99 : .AddValidationAction(
100 68 : [this, checkResSizeInput]()
101 : {
102 34 : return checkResSizeInput(m_resolution_str, "resolution", false);
103 193 : });
104 :
105 : // The same logic was applied in gdalalg_raster_resize.cpp, so we replicate it here for consistency.
106 : AddArg("size", 0,
107 : _("Target size in pixels (or percentage if using '%' suffix)"),
108 386 : &m_size_str)
109 193 : .SetMinCount(2)
110 193 : .SetMaxCount(2)
111 193 : .SetMinValueIncluded(0)
112 193 : .SetRepeatedArgAllowed(false)
113 193 : .SetDisplayHintAboutRepetition(false)
114 386 : .SetMetaVar("<width[%]>,<height[%]>")
115 386 : .SetMutualExclusionGroup("resolution-size")
116 : .AddValidationAction(
117 292 : [this, checkResSizeInput]()
118 339 : { return checkResSizeInput(m_size_str, "size", true); });
119 :
120 386 : AddArg("band-count", 0, _("Number of bands"), &m_bandCount)
121 193 : .SetDefault(m_bandCount)
122 193 : .SetMinValueIncluded(0);
123 193 : AddOutputDataTypeArg(&m_type).SetDefault(m_type);
124 :
125 193 : AddNodataArg(&m_nodata, /* noneAllowed = */ true);
126 :
127 193 : AddArg("burn", 0, _("Burn value"), &m_burnValues);
128 386 : AddArg("crs", 0, _("Set CRS"), &m_crs)
129 386 : .AddHiddenAlias("a_srs")
130 193 : .SetIsCRSArg(/*noneAllowed=*/true);
131 193 : AddBBOXArg(&m_bbox);
132 :
133 : {
134 386 : auto &arg = AddArg("metadata", 0, _("Add metadata item"), &m_metadata)
135 386 : .SetMetaVar("<KEY>=<VALUE>")
136 193 : .SetPackedValuesAllowed(false);
137 56 : arg.AddValidationAction([this, &arg]()
138 249 : { return ParseAndValidateKeyValue(arg); });
139 193 : arg.AddHiddenAlias("mo");
140 : }
141 :
142 193 : const auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
143 193 : CPLAssertNotNull(inputArg);
144 :
145 : AddArg("copy-metadata", 0, _("Copy metadata from input dataset"),
146 386 : &m_copyMetadata)
147 193 : .AddDirectDependency(*inputArg);
148 : AddArg("copy-overviews", 0,
149 386 : _("Create same overview levels as input dataset"), &m_copyOverviews)
150 193 : .AddDirectDependency(*inputArg);
151 193 : }
152 :
153 : /************************************************************************/
154 : /* GDALRasterCreateAlgorithm::RunImpl() */
155 : /************************************************************************/
156 :
157 99 : bool GDALRasterCreateAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
158 : void *pProgressData)
159 : {
160 99 : GDALPipelineStepRunContext stepCtxt;
161 99 : stepCtxt.m_pfnProgress = pfnProgress;
162 99 : stepCtxt.m_pProgressData = pProgressData;
163 99 : return RunPreStepPipelineValidations() && RunStep(stepCtxt);
164 : }
165 :
166 : /************************************************************************/
167 : /* GDALRasterCreateAlgorithm::RunStep() */
168 : /************************************************************************/
169 :
170 102 : bool GDALRasterCreateAlgorithm::RunStep(GDALPipelineStepRunContext &)
171 : {
172 102 : CPLAssert(!m_outputDataset.GetDatasetRef());
173 :
174 102 : if (m_standaloneStep)
175 : {
176 99 : if (m_format.empty())
177 : {
178 : const auto aosFormats =
179 : CPLStringList(GDALGetOutputDriversForDatasetName(
180 43 : m_outputDataset.GetName().c_str(), GDAL_OF_RASTER,
181 : /* bSingleMatch = */ true,
182 43 : /* bWarn = */ true));
183 43 : if (aosFormats.size() != 1)
184 : {
185 1 : ReportError(CE_Failure, CPLE_AppDefined,
186 : "Cannot guess driver for %s",
187 1 : m_outputDataset.GetName().c_str());
188 1 : return false;
189 : }
190 42 : m_format = aosFormats[0];
191 : }
192 : }
193 : else
194 : {
195 3 : m_format = "MEM";
196 : }
197 :
198 202 : OGRSpatialReference oSRS;
199 :
200 101 : GDALGeoTransform gt;
201 101 : bool bGTValid = false;
202 :
203 202 : CPLStringList aosCreationOptions(m_creationOptions);
204 :
205 101 : GDALDataset *poSrcDS = m_inputDataset.empty()
206 101 : ? nullptr
207 36 : : m_inputDataset.front().GetDatasetRef();
208 :
209 101 : constexpr double EPSILON = 1e-5;
210 :
211 : // Process size_str to fill m_size
212 228 : for (size_t i = 0; i < m_size_str.size(); i++)
213 : {
214 130 : const auto &s = m_size_str[i];
215 130 : char *endptr = nullptr;
216 130 : const double val = CPLStrtod(s.c_str(), &endptr);
217 130 : if (endptr &&
218 130 : ((endptr[0] == ' ' && endptr[1] == '%' &&
219 0 : endptr + 2 == s.c_str() + s.size()) ||
220 260 : (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
221 : {
222 : // Percentage
223 15 : if (!poSrcDS)
224 : {
225 1 : ReportError(CE_Failure, CPLE_AppDefined,
226 : "Cannot use percentage size without input dataset");
227 3 : return false;
228 : }
229 14 : const int refSize = (i == 0) ? poSrcDS->GetRasterXSize()
230 7 : : poSrcDS->GetRasterYSize();
231 14 : const double dfSize = std::ceil((refSize * val / 100.0) - EPSILON);
232 : ;
233 14 : if (dfSize > INT_MAX)
234 : {
235 2 : ReportError(CE_Failure, CPLE_AppDefined,
236 : "Computed size is too large");
237 2 : return false;
238 : }
239 12 : m_size.push_back(static_cast<int>(dfSize));
240 : }
241 : else
242 : {
243 115 : m_size.push_back(static_cast<int>(val));
244 : }
245 : }
246 :
247 196 : std::vector<bool> abResIsPercentage(m_resolution_str.size(), false);
248 :
249 : // Process resolution_str to fill m_resolution
250 126 : for (size_t i = 0; i < m_resolution_str.size(); i++)
251 : {
252 30 : const auto &s = m_resolution_str[i];
253 30 : char *endptr = nullptr;
254 30 : const double val = CPLStrtod(s.c_str(), &endptr);
255 :
256 30 : if (endptr &&
257 30 : ((endptr[0] == ' ' && endptr[1] == '%' &&
258 0 : endptr + 2 == s.c_str() + s.size()) ||
259 60 : (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
260 : {
261 : // Percentage
262 14 : if (!poSrcDS)
263 : {
264 2 : ReportError(
265 : CE_Failure, CPLE_AppDefined,
266 : "Cannot use percentage resolution without input dataset");
267 2 : return false;
268 : }
269 12 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
270 : {
271 : const double refRes =
272 12 : (i == 0) ? std::abs(gt.xscale) : std::abs(gt.yscale);
273 12 : const double dfRes = refRes * (val / 100.0);
274 12 : m_resolution.push_back(dfRes);
275 : }
276 : else
277 : {
278 0 : ReportError(CE_Failure, CPLE_AppDefined,
279 : "Cannot get geotransform from input dataset");
280 0 : return false;
281 : }
282 12 : abResIsPercentage[i] = true;
283 : }
284 : else
285 : {
286 16 : m_resolution.push_back(val);
287 16 : abResIsPercentage[i] = false;
288 : }
289 : }
290 :
291 96 : if (poSrcDS)
292 : {
293 34 : if (m_size.empty())
294 : {
295 75 : m_size = std::vector<int>{poSrcDS->GetRasterXSize(),
296 50 : poSrcDS->GetRasterYSize()};
297 : }
298 :
299 34 : bGTValid = poSrcDS->GetGeoTransform(gt) == CE_None;
300 :
301 : // If one of the size is 0, compute it from the other size
302 34 : if (m_size[0] == 0 && m_size[1] > 0)
303 : {
304 1 : if (bGTValid)
305 : {
306 : const double ratio =
307 1 : static_cast<double>(poSrcDS->GetRasterXSize()) /
308 1 : static_cast<double>(poSrcDS->GetRasterYSize());
309 : const double dfWidth =
310 1 : std::ceil(static_cast<double>(m_size[1]) * ratio - EPSILON);
311 1 : if (dfWidth > INT_MAX)
312 : {
313 0 : ReportError(CE_Failure, CPLE_AppDefined,
314 : "Computed width is too large");
315 0 : return false;
316 : }
317 1 : m_size[0] = static_cast<int>(dfWidth);
318 : }
319 : else
320 : {
321 0 : ReportError(CE_Failure, CPLE_AppDefined,
322 : "Cannot get geotransform from input dataset");
323 0 : return false;
324 : }
325 : }
326 33 : else if (m_size[1] == 0 && m_size[0] > 0)
327 : {
328 1 : if (bGTValid)
329 : {
330 : const double ratio =
331 1 : static_cast<double>(poSrcDS->GetRasterYSize()) /
332 1 : static_cast<double>(poSrcDS->GetRasterXSize());
333 : const double dfHeight =
334 1 : std::ceil(static_cast<double>(m_size[0]) * ratio - EPSILON);
335 1 : if (dfHeight > INT_MAX)
336 : {
337 0 : ReportError(CE_Failure, CPLE_AppDefined,
338 : "Computed height is too large");
339 0 : return false;
340 : }
341 1 : m_size[1] = static_cast<int>(dfHeight);
342 : }
343 : else
344 : {
345 0 : m_size[1] = poSrcDS->GetRasterYSize();
346 : }
347 : }
348 :
349 34 : if (!GetArg("band-count")->IsExplicitlySet())
350 : {
351 33 : m_bandCount = poSrcDS->GetRasterCount();
352 : }
353 :
354 34 : if (!GetArg("datatype")->IsExplicitlySet())
355 : {
356 33 : if (m_bandCount > 0)
357 : {
358 : m_type = GDALGetDataTypeName(
359 33 : poSrcDS->GetRasterBand(1)->GetRasterDataType());
360 : }
361 : }
362 :
363 34 : if (m_crs.empty())
364 : {
365 32 : if (const auto poSRS = poSrcDS->GetSpatialRef())
366 32 : oSRS = *poSRS;
367 : }
368 :
369 34 : if (m_nodata.empty() && m_bandCount > 0)
370 : {
371 33 : int bNoData = false;
372 : const double dfNoData =
373 33 : poSrcDS->GetRasterBand(1)->GetNoDataValue(&bNoData);
374 33 : if (bNoData)
375 24 : m_nodata = CPLSPrintf("%.17g", dfNoData);
376 : }
377 :
378 : // Replicate tiling of input datasets for a few popular output formats,
379 : // when compatible, and when the user hasn't specified creation options
380 : // affecting tiling.
381 34 : int nBlockXSize = 0, nBlockYSize = 0;
382 34 : if (m_bandCount > 0)
383 34 : poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
384 :
385 34 : if (EQUAL(m_format.c_str(), "GTIFF") &&
386 17 : aosCreationOptions.FetchNameValue("TILED") == nullptr &&
387 16 : aosCreationOptions.FetchNameValue("BLOCKXSIZE") == nullptr &&
388 67 : aosCreationOptions.FetchNameValue("BLOCKYSIZE") == nullptr &&
389 16 : m_bandCount > 0)
390 : {
391 16 : if (nBlockXSize != poSrcDS->GetRasterXSize() &&
392 16 : (nBlockXSize % 16) == 0 && (nBlockYSize % 16) == 0)
393 : {
394 1 : aosCreationOptions.SetNameValue("TILED", "YES");
395 : aosCreationOptions.SetNameValue("BLOCKXSIZE",
396 1 : CPLSPrintf("%d", nBlockXSize));
397 : aosCreationOptions.SetNameValue("BLOCKYSIZE",
398 1 : CPLSPrintf("%d", nBlockYSize));
399 : }
400 : }
401 18 : else if (EQUAL(m_format.c_str(), "COG") &&
402 19 : aosCreationOptions.FetchNameValue("BLOCKSIZE") == nullptr &&
403 1 : m_bandCount > 0)
404 : {
405 1 : if (nBlockXSize != poSrcDS->GetRasterXSize() &&
406 2 : nBlockXSize == nBlockYSize && nBlockXSize >= 128 &&
407 1 : (nBlockXSize % 16) == 0)
408 : {
409 : aosCreationOptions.SetNameValue("BLOCKSIZE",
410 1 : CPLSPrintf("%d", nBlockXSize));
411 : }
412 : }
413 17 : else if (EQUAL(m_format.c_str(), "GPKG") &&
414 3 : aosCreationOptions.FetchNameValue("BLOCKSIZE") == nullptr &&
415 2 : aosCreationOptions.FetchNameValue("BLOCKXSIZE") == nullptr &&
416 22 : aosCreationOptions.FetchNameValue("BLOCKYSIZE") == nullptr &&
417 2 : m_bandCount > 0)
418 : {
419 2 : if (nBlockXSize != poSrcDS->GetRasterXSize() &&
420 2 : nBlockXSize >= 256 && nBlockXSize <= 4096 &&
421 4 : nBlockYSize >= 256 && nBlockYSize <= 4096)
422 : {
423 : aosCreationOptions.SetNameValue("BLOCKXSIZE",
424 1 : CPLSPrintf("%d", nBlockXSize));
425 : aosCreationOptions.SetNameValue("BLOCKYSIZE",
426 1 : CPLSPrintf("%d", nBlockYSize));
427 : }
428 : }
429 :
430 : // If resolution was explicitly set, then we need to recompute size
431 34 : if (!m_resolution.empty() && bGTValid)
432 : {
433 : // Set resolution from the other axis if 0
434 7 : if (m_resolution[0] == 0)
435 : {
436 1 : if (abResIsPercentage[1])
437 : {
438 2 : m_resolution[0] = (std::abs(gt.xscale) / m_resolution[1]) /
439 1 : std::abs(gt.yscale);
440 : }
441 : else
442 : {
443 0 : m_resolution[0] = m_resolution[1];
444 : }
445 : }
446 :
447 7 : if (m_resolution[1] == 0)
448 : {
449 1 : if (abResIsPercentage[0])
450 : {
451 2 : m_resolution[1] = (std::abs(gt.yscale) / m_resolution[0]) /
452 1 : std::abs(gt.xscale);
453 : }
454 : else
455 : {
456 0 : m_resolution[1] = m_resolution[0];
457 : }
458 : }
459 :
460 7 : const double dfXResRatio = std::abs(gt.xscale) / m_resolution[0];
461 7 : const double dfYResRatio = std::abs(gt.yscale) / m_resolution[1];
462 : const double dfWidth =
463 7 : std::ceil(poSrcDS->GetRasterXSize() * dfXResRatio - EPSILON);
464 : const double dfHeight =
465 7 : std::ceil(poSrcDS->GetRasterYSize() * dfYResRatio - EPSILON);
466 7 : if (dfWidth > INT_MAX || dfHeight > INT_MAX)
467 : {
468 0 : ReportError(CE_Failure, CPLE_AppDefined,
469 : "Computed size is too large");
470 0 : return false;
471 : }
472 7 : m_size = {static_cast<int>(dfWidth), static_cast<int>(dfHeight)};
473 : }
474 : }
475 :
476 : // If size is empty, try resolution from bbox
477 104 : if (m_size.empty() && m_bbox.size() == 4 && m_resolution.size() == 2 &&
478 104 : (m_bbox[3] - m_bbox[1] != 0) && (m_bbox[2] - m_bbox[0] != 0))
479 : {
480 : const double dfWidth =
481 6 : std::ceil((m_bbox[2] - m_bbox[0]) / m_resolution[0] - EPSILON);
482 : const double dfHeight =
483 6 : std::ceil((m_bbox[3] - m_bbox[1]) / m_resolution[1] - EPSILON);
484 6 : if (dfWidth > INT_MAX || dfHeight > INT_MAX)
485 : {
486 3 : ReportError(CE_Failure, CPLE_AppDefined,
487 : "Computed size is too large");
488 3 : return false;
489 : }
490 3 : m_size = {static_cast<int>(dfWidth), static_cast<int>(dfHeight)};
491 : }
492 :
493 93 : if (m_size.empty())
494 : {
495 2 : if (!m_resolution.empty() && m_bbox.empty())
496 : {
497 1 : ReportError(
498 : CE_Failure, CPLE_IllegalArg,
499 : "Cannot use resolution without 'bbox' or 'like' dataset");
500 : }
501 : else
502 : {
503 1 : ReportError(CE_Failure, CPLE_IllegalArg,
504 : "Argument 'size' or 'resolution' or 'like' dataset "
505 : "should be specified");
506 : }
507 2 : return false;
508 : }
509 :
510 : // Guess the size from bbox if only one of the two is specified
511 182 : if (m_size.size() == 2 && (m_size[0] == 0 || m_size[1] == 0) &&
512 5 : !(m_size[0] == 0 && m_size[1] == 0) && m_bbox.size() == 4 &&
513 182 : (m_bbox[3] - m_bbox[1] != 0) && (m_bbox[2] - m_bbox[0] != 0))
514 : {
515 4 : const double ratio = (m_bbox[2] - m_bbox[0]) / (m_bbox[3] - m_bbox[1]);
516 4 : if (m_size[0] == 0)
517 : {
518 2 : double dfWidth = std::ceil(m_size[1] * ratio - EPSILON);
519 2 : if (dfWidth > INT_MAX)
520 : {
521 1 : ReportError(CE_Failure, CPLE_AppDefined,
522 : "Too large computed width");
523 1 : return false;
524 : }
525 1 : m_size[0] = static_cast<int>(dfWidth);
526 : }
527 2 : else if (m_size[1] == 0)
528 : {
529 2 : double dfHeight = std::ceil(m_size[0] / ratio - EPSILON);
530 2 : if (dfHeight > INT_MAX)
531 : {
532 1 : ReportError(CE_Failure, CPLE_AppDefined,
533 : "Too large computed height");
534 1 : return false;
535 : }
536 1 : m_size[1] = static_cast<int>(dfHeight);
537 : }
538 : }
539 :
540 133 : if (!m_burnValues.empty() && m_burnValues.size() != 1 &&
541 44 : static_cast<int>(m_burnValues.size()) != m_bandCount)
542 : {
543 2 : if (m_bandCount == 1)
544 : {
545 1 : ReportError(CE_Failure, CPLE_IllegalArg,
546 : "One value should be provided for argument "
547 : "'burn', given there is one band");
548 : }
549 : else
550 : {
551 1 : ReportError(CE_Failure, CPLE_IllegalArg,
552 : "One or %d values should be provided for argument "
553 : "'burn', given there are %d bands",
554 : m_bandCount, m_bandCount);
555 : }
556 2 : return false;
557 : }
558 :
559 87 : auto poDriver = GetGDALDriverManager()->GetDriverByName(m_format.c_str());
560 87 : if (!poDriver)
561 : {
562 : // shouldn't happen given checks done in GDALAlgorithm
563 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
564 : m_format.c_str());
565 0 : return false;
566 : }
567 :
568 87 : if (m_appendRaster)
569 : {
570 2 : if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS) == nullptr)
571 : {
572 1 : ReportError(CE_Failure, CPLE_NotSupported,
573 : "-append option not supported for driver %s",
574 1 : poDriver->GetDescription());
575 1 : return false;
576 : }
577 1 : aosCreationOptions.SetNameValue("APPEND_SUBDATASET", "YES");
578 : }
579 :
580 : auto poRetDS = std::unique_ptr<GDALDataset>(poDriver->Create(
581 258 : m_outputDataset.GetName().c_str(), m_size[0], m_size[1], m_bandCount,
582 172 : GDALGetDataTypeByName(m_type.c_str()), aosCreationOptions.List()));
583 86 : if (!poRetDS)
584 : {
585 1 : return false;
586 : }
587 :
588 85 : if (!m_crs.empty() && m_crs != "none" && m_crs != "null")
589 : {
590 36 : oSRS.SetFromUserInput(m_crs.c_str());
591 36 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
592 : }
593 :
594 85 : if (!oSRS.IsEmpty())
595 : {
596 68 : if (poRetDS->SetSpatialRef(&oSRS) != CE_None)
597 : {
598 1 : ReportError(CE_Failure, CPLE_AppDefined, "Setting CRS failed");
599 1 : return false;
600 : }
601 : }
602 :
603 84 : if (!m_bbox.empty())
604 : {
605 42 : if (poRetDS->GetRasterXSize() == 0 || poRetDS->GetRasterYSize() == 0)
606 : {
607 1 : ReportError(CE_Failure, CPLE_AppDefined,
608 : "Cannot set extent because one of dataset height or "
609 : "width is null");
610 1 : return false;
611 : }
612 41 : bGTValid = true;
613 41 : gt.xorig = m_bbox[0];
614 41 : gt.xscale = (m_bbox[2] - m_bbox[0]) / poRetDS->GetRasterXSize();
615 41 : gt.xrot = 0;
616 41 : gt.yorig = m_bbox[3];
617 41 : gt.yrot = 0;
618 41 : gt.yscale = -(m_bbox[3] - m_bbox[1]) / poRetDS->GetRasterYSize();
619 : }
620 83 : if (bGTValid)
621 : {
622 74 : if (poRetDS->SetGeoTransform(gt) != CE_None)
623 : {
624 1 : ReportError(CE_Failure, CPLE_AppDefined, "Setting extent failed");
625 1 : return false;
626 : }
627 : }
628 :
629 82 : if (!m_nodata.empty() && !EQUAL(m_nodata.c_str(), "none"))
630 : {
631 158 : for (int i = 0; i < poRetDS->GetRasterCount(); ++i)
632 : {
633 105 : bool bCannotBeExactlyRepresented = false;
634 105 : if (poRetDS->GetRasterBand(i + 1)->SetNoDataValueAsString(
635 105 : m_nodata.c_str(), &bCannotBeExactlyRepresented) != CE_None)
636 : {
637 1 : if (bCannotBeExactlyRepresented)
638 : {
639 1 : ReportError(CE_Failure, CPLE_AppDefined,
640 : "Setting nodata value failed as it cannot be "
641 : "represented on its data type");
642 : }
643 : else
644 : {
645 0 : ReportError(CE_Failure, CPLE_AppDefined,
646 : "Setting nodata value failed");
647 : }
648 1 : return false;
649 : }
650 : }
651 : }
652 :
653 81 : if (m_copyMetadata)
654 : {
655 :
656 : // This should never happen because of the dependency set
657 1 : CPLAssertNotNull(poSrcDS);
658 :
659 : {
660 1 : const CPLStringList aosDomains(poSrcDS->GetMetadataDomainList());
661 4 : for (const char *domain : aosDomains)
662 : {
663 3 : if (!EQUAL(domain, GDAL_MDD_IMAGE_STRUCTURE))
664 : {
665 4 : if (poRetDS->SetMetadata(poSrcDS->GetMetadata(domain),
666 4 : domain) != CE_None)
667 : {
668 0 : ReportError(CE_Failure, CPLE_AppDefined,
669 : "Cannot copy '%s' metadata domain", domain);
670 0 : return false;
671 : }
672 : }
673 : }
674 : }
675 3 : for (int i = 0; i < m_bandCount; ++i)
676 : {
677 : const CPLStringList aosDomains(
678 2 : poSrcDS->GetRasterBand(i + 1)->GetMetadataDomainList());
679 3 : for (const char *domain : aosDomains)
680 : {
681 1 : if (!EQUAL(domain, GDAL_MDD_IMAGE_STRUCTURE))
682 : {
683 2 : if (poRetDS->GetRasterBand(i + 1)->SetMetadata(
684 1 : poSrcDS->GetRasterBand(i + 1)->GetMetadata(domain),
685 2 : domain) != CE_None)
686 : {
687 0 : ReportError(
688 : CE_Failure, CPLE_AppDefined,
689 : "Cannot copy '%s' metadata domain for band %d",
690 : domain, i + 1);
691 0 : return false;
692 : }
693 : }
694 : }
695 : }
696 : }
697 :
698 162 : const CPLStringList aosMD(m_metadata);
699 109 : for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
700 : {
701 28 : if (poRetDS->SetMetadataItem(key, value) != CE_None)
702 : {
703 0 : ReportError(CE_Failure, CPLE_AppDefined,
704 : "SetMetadataItem('%s', '%s') failed", key, value);
705 0 : return false;
706 : }
707 : }
708 :
709 81 : if (m_copyOverviews && m_bandCount > 0)
710 : {
711 : // This should never happen because of the dependency set
712 2 : CPLAssertNotNull(poSrcDS);
713 :
714 3 : if (poSrcDS->GetRasterXSize() != poRetDS->GetRasterXSize() ||
715 1 : poSrcDS->GetRasterYSize() != poRetDS->GetRasterYSize())
716 : {
717 1 : ReportError(CE_Failure, CPLE_AppDefined,
718 : "Argument 'copy-overviews' can only be set when the "
719 : "input and output datasets have the same dimension");
720 1 : return false;
721 : }
722 : const int nOverviewCount =
723 1 : poSrcDS->GetRasterBand(1)->GetOverviewCount();
724 1 : std::vector<int> anLevels;
725 2 : for (int i = 0; i < nOverviewCount; ++i)
726 : {
727 1 : const auto poOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(i);
728 1 : const int nOvrFactor = GDALComputeOvFactor(
729 : poOvrBand->GetXSize(), poSrcDS->GetRasterXSize(),
730 1 : poOvrBand->GetYSize(), poSrcDS->GetRasterYSize());
731 1 : anLevels.push_back(nOvrFactor);
732 : }
733 2 : if (poRetDS->BuildOverviews(
734 1 : "NONE", nOverviewCount, anLevels.data(),
735 : /* nListBands = */ 0, /* panBandList = */ nullptr,
736 : /* pfnProgress = */ nullptr, /* pProgressData = */ nullptr,
737 1 : /* options = */ nullptr) != CE_None)
738 : {
739 0 : ReportError(CE_Failure, CPLE_AppDefined,
740 : "Creating overview(s) failed");
741 0 : return false;
742 : }
743 : }
744 :
745 80 : if (!m_burnValues.empty())
746 : {
747 138 : for (int i = 0; i < m_bandCount; ++i)
748 : {
749 90 : const int burnValueIdx = m_burnValues.size() == 1 ? 0 : i;
750 90 : const auto poDstBand = poRetDS->GetRasterBand(i + 1);
751 : // cppcheck-suppress negativeContainerIndex
752 90 : if (poDstBand->Fill(m_burnValues[burnValueIdx]) != CE_None)
753 : {
754 0 : ReportError(CE_Failure, CPLE_AppDefined,
755 : "Setting burn value failed");
756 0 : return false;
757 : }
758 : }
759 48 : if (poRetDS->FlushCache(false) != CE_None)
760 : {
761 0 : ReportError(CE_Failure, CPLE_AppDefined,
762 : "Setting burn value failed");
763 0 : return false;
764 : }
765 : }
766 :
767 80 : m_outputDataset.Set(std::move(poRetDS));
768 :
769 80 : return true;
770 : }
771 :
772 : GDALRasterCreateAlgorithmStandalone::~GDALRasterCreateAlgorithmStandalone() =
773 : default;
774 :
775 : //! @endcond
|