Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: GDAL Raster creation utility
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2020, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_string.h"
14 : #include "gdal_version.h"
15 : #include "gdal_priv.h"
16 : #include "gdal.h"
17 : #include "commonutils.h"
18 : #include "gdalargumentparser.h"
19 : #include "ogr_spatialref.h"
20 :
21 : #include <cstdlib>
22 : #include <memory>
23 : #include <vector>
24 :
25 : /**
26 : * @brief Makes sure the GDAL library is properly cleaned up before exiting.
27 : * @param nCode exit code
28 : * @todo Move to API
29 : */
30 2 : static void GDALExit(int nCode)
31 : {
32 2 : GDALDestroy();
33 2 : exit(nCode);
34 : }
35 :
36 : /************************************************************************/
37 : /* GDALCreateOptions */
38 : /************************************************************************/
39 :
40 : struct GDALCreateOptions
41 : {
42 : int nBandCount = -1;
43 : int nPixels = 0;
44 : bool bPixelsSet{false};
45 : int nLines = 0;
46 : GDALDataType eDT = GDT_Unknown;
47 : double dfULX = 0;
48 : double dfULY = 0;
49 : double dfLRX = 0;
50 : double dfLRY = 0;
51 : int nULCounter{0};
52 : bool bGeoTransform = false;
53 : std::string osOutputSRS;
54 : CPLStringList aosMetadata;
55 : std::vector<double> adfBurnValues;
56 : bool bQuiet = false;
57 : bool bSetNoData = false;
58 : std::string osNoData;
59 : std::string osOutputFilename;
60 : std::string osInputFilename;
61 : std::string osFormat;
62 : CPLStringList aosCreateOptions;
63 : };
64 :
65 : /************************************************************************/
66 : /* GDALCreateAppOptionsGetParser() */
67 : /************************************************************************/
68 :
69 : static std::unique_ptr<GDALArgumentParser>
70 11 : GDALCreateAppOptionsGetParser(GDALCreateOptions *psOptions)
71 : {
72 : auto argParser = std::make_unique<GDALArgumentParser>(
73 11 : "gdal_create", /* bForBinary */ true);
74 :
75 11 : argParser->add_description(
76 11 : _("Create a raster file (without source dataset)."));
77 :
78 11 : argParser->add_epilog(_(
79 : "For more details, consult the full documentation for the gdal_create "
80 11 : "utility: http://gdal.org/gdal_create.html"));
81 :
82 11 : argParser->add_output_type_argument(psOptions->eDT);
83 :
84 11 : argParser->add_output_format_argument(psOptions->osFormat);
85 :
86 11 : argParser->add_argument("-outsize")
87 22 : .metavar("<xsize> <ysize>")
88 11 : .nargs(2)
89 11 : .scan<'i', int>()
90 : .action(
91 24 : [psOptions](const std::string &s)
92 : {
93 12 : if (!psOptions->bPixelsSet)
94 : {
95 6 : psOptions->nPixels = atoi(s.c_str());
96 6 : psOptions->bPixelsSet = true;
97 : }
98 : else
99 : {
100 6 : psOptions->nLines = atoi(s.c_str());
101 : }
102 11 : })
103 11 : .help(_("Set the size of the output file."));
104 :
105 11 : argParser->add_argument("-bands")
106 22 : .metavar("<count>")
107 11 : .store_into(psOptions->nBandCount)
108 11 : .help(_("Set the number of bands in the output file."));
109 :
110 22 : argParser->add_argument("-burn").metavar("<value>").append().help(
111 : _("A fixed value to burn into a band. A list of "
112 11 : "-burn options can be supplied, one per band being written to."));
113 :
114 11 : argParser->add_argument("-a_srs")
115 22 : .metavar("<srs_def>")
116 11 : .store_into(psOptions->osOutputSRS)
117 11 : .help(_("Override the projection for the output file. "));
118 :
119 11 : argParser->add_argument("-a_ullr")
120 22 : .metavar("<ulx> <uly> <lrx> <lry>")
121 11 : .scan<'g', double>()
122 11 : .nargs(4)
123 : .action(
124 27 : [psOptions](const std::string &s)
125 : {
126 12 : switch (psOptions->nULCounter++)
127 : {
128 3 : case 0:
129 3 : psOptions->bGeoTransform = true;
130 3 : psOptions->dfULX = CPLAtofM(s.c_str());
131 3 : break;
132 3 : case 1:
133 3 : psOptions->dfULY = CPLAtofM(s.c_str());
134 3 : break;
135 3 : case 2:
136 3 : psOptions->dfLRX = CPLAtofM(s.c_str());
137 3 : break;
138 3 : case 3:
139 3 : psOptions->dfLRY = CPLAtof(s.c_str());
140 3 : break;
141 : }
142 11 : })
143 11 : .help(_("Assign the georeferenced bounds of the output file. "));
144 :
145 11 : argParser->add_argument("-a_nodata")
146 22 : .metavar("<value>")
147 11 : .scan<'g', double>()
148 : .action(
149 4 : [psOptions](const std::string &s)
150 : {
151 4 : psOptions->bSetNoData = true;
152 4 : psOptions->osNoData = s;
153 11 : })
154 11 : .help(_("Assign a specified nodata value to output bands."));
155 :
156 11 : argParser->add_metadata_item_options_argument(psOptions->aosMetadata);
157 :
158 11 : argParser->add_creation_options_argument(psOptions->aosCreateOptions);
159 :
160 11 : argParser->add_quiet_argument(&psOptions->bQuiet);
161 :
162 11 : argParser->add_argument("-if")
163 22 : .metavar("<input_dataset>")
164 11 : .store_into(psOptions->osInputFilename)
165 : .help(_("Name of GDAL input dataset that serves as a template for "
166 : "default values of options -outsize, -bands, -ot, -a_srs, "
167 11 : "-a_ullr and -a_nodata."));
168 :
169 11 : argParser->add_argument("out_dataset")
170 22 : .metavar("<out_dataset>")
171 11 : .store_into(psOptions->osOutputFilename)
172 11 : .help(_("Name of the output dataset to create."));
173 :
174 11 : return argParser;
175 : }
176 :
177 : /************************************************************************/
178 : /* main() */
179 : /************************************************************************/
180 :
181 11 : MAIN_START(argc, argv)
182 :
183 : {
184 : /* Check strict compilation and runtime library version as we use C++ API */
185 11 : if (!GDAL_CHECK_VERSION(argv[0]))
186 0 : exit(1);
187 :
188 11 : EarlySetConfigOptions(argc, argv);
189 :
190 : /* -------------------------------------------------------------------- */
191 : /* Register standard GDAL drivers, and process generic GDAL */
192 : /* command options. */
193 : /* -------------------------------------------------------------------- */
194 11 : GDALAllRegister();
195 :
196 11 : argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
197 11 : if (argc < 1)
198 0 : GDALExit(-argc);
199 :
200 11 : if (argc < 2)
201 : {
202 : try
203 : {
204 0 : GDALCreateOptions sOptions;
205 0 : auto argParser = GDALCreateAppOptionsGetParser(&sOptions);
206 0 : fprintf(stderr, "%s\n", argParser->usage().c_str());
207 : }
208 0 : catch (const std::exception &err)
209 : {
210 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
211 0 : err.what());
212 : }
213 0 : CSLDestroy(argv);
214 0 : GDALExit(1);
215 : }
216 :
217 19 : GDALCreateOptions sOptions;
218 :
219 19 : CPLStringList aosArgv;
220 113 : for (int iArg = 1; iArg < argc; iArg++)
221 : {
222 102 : if (iArg + 1 < argc && EQUAL(argv[iArg], "-burn"))
223 : {
224 4 : ++iArg;
225 : while (true)
226 : {
227 5 : if (strchr(argv[iArg], ' '))
228 : {
229 : const CPLStringList aosTokens(
230 2 : CSLTokenizeString(argv[iArg]));
231 3 : for (int i = 0; i < aosTokens.size(); i++)
232 : {
233 2 : char *endptr = nullptr;
234 2 : sOptions.adfBurnValues.push_back(
235 2 : CPLStrtodM(aosTokens[i], &endptr));
236 2 : if (endptr != aosTokens[i] + strlen(aosTokens[i]))
237 : {
238 0 : fprintf(stderr, "Invalid value for -burn\n");
239 0 : CSLDestroy(argv);
240 0 : GDALExit(1);
241 : }
242 : }
243 : }
244 : else
245 : {
246 4 : char *endptr = nullptr;
247 4 : sOptions.adfBurnValues.push_back(
248 4 : CPLStrtodM(argv[iArg], &endptr));
249 4 : if (endptr != argv[iArg] + strlen(argv[iArg]))
250 : {
251 0 : fprintf(stderr, "Invalid value for -burn\n");
252 0 : CSLDestroy(argv);
253 0 : GDALExit(1);
254 : }
255 : }
256 10 : if (iArg + 1 < argc &&
257 5 : CPLGetValueType(argv[iArg + 1]) != CPL_VALUE_STRING)
258 : {
259 1 : ++iArg;
260 : }
261 : else
262 : {
263 4 : break;
264 : }
265 5 : }
266 : }
267 : else
268 : {
269 98 : aosArgv.AddString(argv[iArg]);
270 : }
271 : }
272 11 : CSLDestroy(argv);
273 :
274 : try
275 : {
276 :
277 21 : auto argParser = GDALCreateAppOptionsGetParser(&sOptions);
278 11 : argParser->parse_args_without_binary_name(aosArgv.List());
279 : }
280 0 : catch (const std::exception &error)
281 : {
282 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
283 0 : GDALExit(1);
284 : }
285 :
286 10 : double adfGeoTransform[6] = {0, 1, 0, 0, 0, 1};
287 10 : if (sOptions.bGeoTransform && sOptions.nPixels > 0 && sOptions.nLines > 0)
288 : {
289 3 : adfGeoTransform[0] = sOptions.dfULX;
290 3 : adfGeoTransform[1] =
291 3 : (sOptions.dfLRX - sOptions.dfULX) / sOptions.nPixels;
292 3 : adfGeoTransform[2] = 0;
293 3 : adfGeoTransform[3] = sOptions.dfULY;
294 3 : adfGeoTransform[4] = 0;
295 3 : adfGeoTransform[5] =
296 3 : (sOptions.dfLRY - sOptions.dfULY) / sOptions.nLines;
297 : }
298 :
299 0 : std::unique_ptr<GDALDataset> poInputDS;
300 10 : if (!sOptions.osInputFilename.empty())
301 : {
302 4 : poInputDS.reset(
303 : GDALDataset::Open(sOptions.osInputFilename.c_str(),
304 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
305 4 : if (poInputDS == nullptr)
306 : {
307 1 : GDALExit(1);
308 : }
309 3 : if (sOptions.nPixels == 0)
310 : {
311 2 : sOptions.nPixels = poInputDS->GetRasterXSize();
312 2 : sOptions.nLines = poInputDS->GetRasterYSize();
313 : }
314 3 : if (sOptions.nBandCount < 0)
315 : {
316 2 : sOptions.nBandCount = poInputDS->GetRasterCount();
317 : }
318 3 : if (sOptions.eDT == GDT_Unknown && poInputDS->GetRasterCount() > 0)
319 : {
320 3 : sOptions.eDT = poInputDS->GetRasterBand(1)->GetRasterDataType();
321 : }
322 3 : if (sOptions.osOutputSRS.empty())
323 : {
324 3 : sOptions.osOutputSRS = poInputDS->GetProjectionRef();
325 : }
326 3 : if (!(sOptions.bGeoTransform && sOptions.nPixels > 0 &&
327 0 : sOptions.nLines > 0))
328 : {
329 3 : if (poInputDS->GetGeoTransform(adfGeoTransform) == CE_None)
330 : {
331 2 : sOptions.bGeoTransform = true;
332 : }
333 : }
334 3 : if (!sOptions.bSetNoData && poInputDS->GetRasterCount() > 0)
335 : {
336 2 : if (sOptions.eDT == GDT_Int64)
337 : {
338 : int noData;
339 : const auto nNoDataValue =
340 0 : poInputDS->GetRasterBand(1)->GetNoDataValueAsInt64(&noData);
341 0 : sOptions.bSetNoData = noData;
342 0 : if (sOptions.bSetNoData)
343 : sOptions.osNoData = CPLSPrintf(
344 0 : CPL_FRMT_GIB, static_cast<GIntBig>(nNoDataValue));
345 : }
346 2 : else if (sOptions.eDT == GDT_UInt64)
347 : {
348 : int noData;
349 : const auto nNoDataValue =
350 0 : poInputDS->GetRasterBand(1)->GetNoDataValueAsUInt64(
351 0 : &noData);
352 0 : sOptions.bSetNoData = noData;
353 0 : if (sOptions.bSetNoData)
354 : sOptions.osNoData = CPLSPrintf(
355 0 : CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoDataValue));
356 : }
357 : else
358 : {
359 : int noData;
360 : const double dfNoDataValue =
361 2 : poInputDS->GetRasterBand(1)->GetNoDataValue(&noData);
362 2 : sOptions.bSetNoData = noData;
363 2 : if (sOptions.bSetNoData)
364 0 : sOptions.osNoData = CPLSPrintf("%.18g", dfNoDataValue);
365 : }
366 : }
367 : }
368 :
369 18 : GDALDriverH hDriver = GDALGetDriverByName(
370 9 : sOptions.osFormat.empty()
371 16 : ? GetOutputDriverForRaster(sOptions.osOutputFilename.c_str())
372 7 : .c_str()
373 2 : : sOptions.osFormat.c_str());
374 :
375 9 : if (hDriver == nullptr)
376 : {
377 0 : fprintf(stderr, "Output driver not found.\n");
378 0 : GDALExit(1);
379 : }
380 : const bool bHasCreate =
381 9 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) != nullptr;
382 11 : if (!bHasCreate &&
383 2 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) == nullptr)
384 : {
385 1 : fprintf(stderr, "This driver has no creation capabilities.\n");
386 1 : GDALExit(1);
387 : }
388 8 : GDALDriverH hTmpDriver = GDALGetDriverByName("MEM");
389 8 : if (!bHasCreate && hTmpDriver == nullptr)
390 : {
391 0 : fprintf(stderr, "MEM driver not available.\n");
392 0 : GDALExit(1);
393 : }
394 :
395 8 : if (sOptions.nPixels != 0 && sOptions.eDT == GDT_Unknown)
396 : {
397 1 : sOptions.eDT = GDT_Byte;
398 : }
399 8 : if (sOptions.nBandCount < 0)
400 : {
401 2 : sOptions.nBandCount = sOptions.eDT == GDT_Unknown ? 0 : 1;
402 : }
403 15 : GDALDatasetH hDS = GDALCreate(
404 : bHasCreate ? hDriver : hTmpDriver, sOptions.osOutputFilename.c_str(),
405 : sOptions.nPixels, sOptions.nLines, sOptions.nBandCount, sOptions.eDT,
406 7 : bHasCreate ? sOptions.aosCreateOptions.List() : nullptr);
407 :
408 8 : if (hDS == nullptr)
409 : {
410 0 : GDALExit(1);
411 : }
412 :
413 13 : if (!sOptions.osOutputSRS.empty() &&
414 5 : !EQUAL(sOptions.osOutputSRS.c_str(), "NONE"))
415 : {
416 10 : OGRSpatialReference oSRS;
417 5 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
418 :
419 5 : if (oSRS.SetFromUserInput(sOptions.osOutputSRS.c_str()) != OGRERR_NONE)
420 : {
421 0 : CPLError(CE_Failure, CPLE_AppDefined,
422 : "Failed to process SRS definition: %s",
423 : sOptions.osOutputSRS.c_str());
424 0 : GDALExit(1);
425 : }
426 :
427 5 : char *pszSRS = nullptr;
428 5 : oSRS.exportToWkt(&pszSRS);
429 :
430 5 : if (GDALSetProjection(hDS, pszSRS) != CE_None)
431 : {
432 0 : CPLFree(pszSRS);
433 0 : GDALClose(hDS);
434 0 : GDALExit(1);
435 : }
436 5 : CPLFree(pszSRS);
437 : }
438 8 : if (sOptions.bGeoTransform)
439 : {
440 5 : if (sOptions.nPixels == 0)
441 : {
442 0 : fprintf(stderr,
443 : "-outsize must be specified when -a_ullr is used.\n");
444 0 : GDALClose(hDS);
445 0 : GDALExit(1);
446 : }
447 5 : if (GDALSetGeoTransform(hDS, adfGeoTransform) != CE_None)
448 : {
449 0 : GDALClose(hDS);
450 0 : GDALExit(1);
451 : }
452 : }
453 3 : else if (poInputDS && poInputDS->GetGCPCount() > 0)
454 : {
455 2 : GDALDataset::FromHandle(hDS)->SetGCPs(poInputDS->GetGCPCount(),
456 1 : poInputDS->GetGCPs(),
457 1 : poInputDS->GetGCPSpatialRef());
458 : }
459 :
460 8 : if (!sOptions.aosMetadata.empty())
461 : {
462 3 : GDALSetMetadata(hDS, sOptions.aosMetadata.List(), nullptr);
463 : }
464 8 : const int nBands = GDALGetRasterCount(hDS);
465 8 : if (sOptions.bSetNoData)
466 : {
467 15 : for (int i = 0; i < nBands; i++)
468 : {
469 11 : auto hBand = GDALGetRasterBand(hDS, i + 1);
470 11 : if (sOptions.eDT == GDT_Int64)
471 : {
472 0 : GDALSetRasterNoDataValueAsInt64(
473 0 : hBand, static_cast<int64_t>(std::strtoll(
474 : sOptions.osNoData.c_str(), nullptr, 10)));
475 : }
476 11 : else if (sOptions.eDT == GDT_UInt64)
477 : {
478 0 : GDALSetRasterNoDataValueAsUInt64(
479 0 : hBand, static_cast<uint64_t>(std::strtoull(
480 : sOptions.osNoData.c_str(), nullptr, 10)));
481 : }
482 : else
483 : {
484 11 : GDALSetRasterNoDataValue(hBand,
485 : CPLAtofM(sOptions.osNoData.c_str()));
486 : }
487 : }
488 : }
489 8 : if (!sOptions.adfBurnValues.empty())
490 : {
491 12 : for (int i = 0; i < nBands; i++)
492 : {
493 9 : GDALFillRaster(GDALGetRasterBand(hDS, i + 1),
494 9 : i < static_cast<int>(sOptions.adfBurnValues.size())
495 6 : ? sOptions.adfBurnValues[i]
496 3 : : sOptions.adfBurnValues.back(),
497 : 0);
498 : }
499 : }
500 :
501 8 : bool bHasGotErr = false;
502 8 : if (!bHasCreate)
503 : {
504 2 : GDALDatasetH hOutDS = GDALCreateCopy(
505 : hDriver, sOptions.osOutputFilename.c_str(), hDS, false,
506 1 : sOptions.aosCreateOptions.List(),
507 1 : sOptions.bQuiet ? GDALDummyProgress : GDALTermProgress, nullptr);
508 1 : if (hOutDS == nullptr)
509 : {
510 0 : GDALClose(hDS);
511 0 : GDALExit(1);
512 : }
513 1 : if (GDALClose(hOutDS) != CE_None)
514 : {
515 0 : bHasGotErr = true;
516 : }
517 : }
518 :
519 8 : const bool bWasFailureBefore = (CPLGetLastErrorType() == CE_Failure);
520 8 : if (GDALClose(hDS) != CE_None)
521 0 : bHasGotErr = true;
522 8 : if (!bWasFailureBefore && CPLGetLastErrorType() == CE_Failure)
523 : {
524 0 : bHasGotErr = true;
525 : }
526 :
527 8 : return bHasGotErr ? 1 : 0;
528 : }
529 :
530 0 : MAIN_END
|