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 3 : static void GDALExit(int nCode)
31 : {
32 3 : GDALDestroy();
33 3 : 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 10 : GDALCreateAppOptionsGetParser(GDALCreateOptions *psOptions)
71 : {
72 : auto argParser = std::make_unique<GDALArgumentParser>(
73 10 : "gdal_create", /* bForBinary */ true);
74 :
75 10 : argParser->add_description(
76 10 : _("Create a raster file (without source dataset)."));
77 :
78 10 : argParser->add_epilog(_(
79 : "For more details, consult the full documentation for the gdal_create "
80 10 : "utility: http://gdal.org/gdal_create.html"));
81 :
82 10 : argParser->add_output_type_argument(psOptions->eDT);
83 :
84 10 : argParser->add_output_format_argument(psOptions->osFormat);
85 :
86 10 : argParser->add_argument("-outsize")
87 20 : .metavar("<xsize> <ysize>")
88 10 : .nargs(2)
89 10 : .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 10 : })
103 10 : .help(_("Set the size of the output file."));
104 :
105 10 : argParser->add_argument("-bands")
106 20 : .metavar("<count>")
107 10 : .store_into(psOptions->nBandCount)
108 10 : .help(_("Set the number of bands in the output file."));
109 :
110 20 : argParser->add_argument("-burn").metavar("<value>").append().help(
111 : _("A fixed value to burn into a band. A list of "
112 10 : "-burn options can be supplied, one per band being written to."));
113 :
114 10 : argParser->add_argument("-a_srs")
115 20 : .metavar("<srs_def>")
116 10 : .store_into(psOptions->osOutputSRS)
117 10 : .help(_("Override the projection for the output file. "));
118 :
119 10 : argParser->add_argument("-a_ullr")
120 20 : .metavar("<ulx> <uly> <lrx> <lry>")
121 10 : .scan<'g', double>()
122 10 : .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 10 : })
143 10 : .help(_("Assign the georeferenced bounds of the output file. "));
144 :
145 10 : argParser->add_argument("-a_nodata")
146 20 : .metavar("<value>")
147 10 : .scan<'g', double>()
148 : .action(
149 4 : [psOptions](const std::string &s)
150 : {
151 4 : psOptions->bSetNoData = true;
152 4 : psOptions->osNoData = s;
153 10 : })
154 10 : .help(_("Assign a specified nodata value to output bands."));
155 :
156 10 : argParser->add_metadata_item_options_argument(psOptions->aosMetadata);
157 :
158 10 : argParser->add_creation_options_argument(psOptions->aosCreateOptions);
159 :
160 10 : argParser->add_quiet_argument(&psOptions->bQuiet);
161 :
162 10 : argParser->add_argument("-if")
163 20 : .metavar("<input_dataset>")
164 10 : .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 10 : "-a_ullr and -a_nodata."));
168 :
169 10 : argParser->add_argument("out_dataset")
170 20 : .metavar("<out_dataset>")
171 10 : .store_into(psOptions->osOutputFilename)
172 10 : .help(_("Name of the output dataset to create."));
173 :
174 10 : 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 1 : GDALExit(-argc);
199 :
200 10 : 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 18 : GDALCreateOptions sOptions;
218 :
219 18 : CPLStringList aosArgv;
220 111 : for (int iArg = 1; iArg < argc; iArg++)
221 : {
222 101 : 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 97 : aosArgv.AddString(argv[iArg]);
270 : }
271 : }
272 10 : CSLDestroy(argv);
273 :
274 : try
275 : {
276 :
277 20 : auto argParser = GDALCreateAppOptionsGetParser(&sOptions);
278 10 : 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 : GDALGeoTransform gt;
287 10 : if (sOptions.bGeoTransform && sOptions.nPixels > 0 && sOptions.nLines > 0)
288 : {
289 3 : gt[0] = sOptions.dfULX;
290 3 : gt[1] = (sOptions.dfLRX - sOptions.dfULX) / sOptions.nPixels;
291 3 : gt[2] = 0;
292 3 : gt[3] = sOptions.dfULY;
293 3 : gt[4] = 0;
294 3 : gt[5] = (sOptions.dfLRY - sOptions.dfULY) / sOptions.nLines;
295 : }
296 :
297 0 : std::unique_ptr<GDALDataset> poInputDS;
298 10 : if (!sOptions.osInputFilename.empty())
299 : {
300 4 : poInputDS.reset(
301 : GDALDataset::Open(sOptions.osInputFilename.c_str(),
302 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
303 4 : if (poInputDS == nullptr)
304 : {
305 1 : GDALExit(1);
306 : }
307 3 : if (sOptions.nPixels == 0)
308 : {
309 2 : sOptions.nPixels = poInputDS->GetRasterXSize();
310 2 : sOptions.nLines = poInputDS->GetRasterYSize();
311 : }
312 3 : if (sOptions.nBandCount < 0)
313 : {
314 2 : sOptions.nBandCount = poInputDS->GetRasterCount();
315 : }
316 3 : if (sOptions.eDT == GDT_Unknown && poInputDS->GetRasterCount() > 0)
317 : {
318 3 : sOptions.eDT = poInputDS->GetRasterBand(1)->GetRasterDataType();
319 : }
320 3 : if (sOptions.osOutputSRS.empty())
321 : {
322 3 : sOptions.osOutputSRS = poInputDS->GetProjectionRef();
323 : }
324 3 : if (!(sOptions.bGeoTransform && sOptions.nPixels > 0 &&
325 0 : sOptions.nLines > 0))
326 : {
327 3 : if (poInputDS->GetGeoTransform(gt) == CE_None)
328 : {
329 2 : sOptions.bGeoTransform = true;
330 : }
331 : }
332 3 : if (!sOptions.bSetNoData && poInputDS->GetRasterCount() > 0)
333 : {
334 2 : if (sOptions.eDT == GDT_Int64)
335 : {
336 : int noData;
337 : const auto nNoDataValue =
338 0 : poInputDS->GetRasterBand(1)->GetNoDataValueAsInt64(&noData);
339 0 : sOptions.bSetNoData = noData;
340 0 : if (sOptions.bSetNoData)
341 : sOptions.osNoData = CPLSPrintf(
342 0 : CPL_FRMT_GIB, static_cast<GIntBig>(nNoDataValue));
343 : }
344 2 : else if (sOptions.eDT == GDT_UInt64)
345 : {
346 : int noData;
347 : const auto nNoDataValue =
348 0 : poInputDS->GetRasterBand(1)->GetNoDataValueAsUInt64(
349 0 : &noData);
350 0 : sOptions.bSetNoData = noData;
351 0 : if (sOptions.bSetNoData)
352 : sOptions.osNoData = CPLSPrintf(
353 0 : CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoDataValue));
354 : }
355 : else
356 : {
357 : int noData;
358 : const double dfNoDataValue =
359 2 : poInputDS->GetRasterBand(1)->GetNoDataValue(&noData);
360 2 : sOptions.bSetNoData = noData;
361 2 : if (sOptions.bSetNoData)
362 0 : sOptions.osNoData = CPLSPrintf("%.18g", dfNoDataValue);
363 : }
364 : }
365 : }
366 :
367 18 : GDALDriverH hDriver = GDALGetDriverByName(
368 9 : sOptions.osFormat.empty()
369 16 : ? GetOutputDriverForRaster(sOptions.osOutputFilename.c_str())
370 7 : .c_str()
371 2 : : sOptions.osFormat.c_str());
372 :
373 9 : if (hDriver == nullptr)
374 : {
375 0 : fprintf(stderr, "Output driver not found.\n");
376 0 : GDALExit(1);
377 : }
378 : const bool bHasCreate =
379 9 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) != nullptr;
380 11 : if (!bHasCreate &&
381 2 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) == nullptr)
382 : {
383 1 : fprintf(stderr, "This driver has no creation capabilities.\n");
384 1 : GDALExit(1);
385 : }
386 8 : GDALDriverH hTmpDriver = GDALGetDriverByName("MEM");
387 8 : if (!bHasCreate && hTmpDriver == nullptr)
388 : {
389 0 : fprintf(stderr, "MEM driver not available.\n");
390 0 : GDALExit(1);
391 : }
392 :
393 8 : if (sOptions.nPixels != 0 && sOptions.eDT == GDT_Unknown)
394 : {
395 1 : sOptions.eDT = GDT_Byte;
396 : }
397 8 : if (sOptions.nBandCount < 0)
398 : {
399 2 : sOptions.nBandCount = sOptions.eDT == GDT_Unknown ? 0 : 1;
400 : }
401 15 : GDALDatasetH hDS = GDALCreate(
402 : bHasCreate ? hDriver : hTmpDriver, sOptions.osOutputFilename.c_str(),
403 : sOptions.nPixels, sOptions.nLines, sOptions.nBandCount, sOptions.eDT,
404 7 : bHasCreate ? sOptions.aosCreateOptions.List() : nullptr);
405 :
406 8 : if (hDS == nullptr)
407 : {
408 0 : GDALExit(1);
409 : }
410 :
411 13 : if (!sOptions.osOutputSRS.empty() &&
412 5 : !EQUAL(sOptions.osOutputSRS.c_str(), "NONE"))
413 : {
414 10 : OGRSpatialReference oSRS;
415 5 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
416 :
417 5 : if (oSRS.SetFromUserInput(sOptions.osOutputSRS.c_str()) != OGRERR_NONE)
418 : {
419 0 : CPLError(CE_Failure, CPLE_AppDefined,
420 : "Failed to process SRS definition: %s",
421 : sOptions.osOutputSRS.c_str());
422 0 : GDALExit(1);
423 : }
424 :
425 5 : char *pszSRS = nullptr;
426 5 : oSRS.exportToWkt(&pszSRS);
427 :
428 5 : if (GDALSetProjection(hDS, pszSRS) != CE_None)
429 : {
430 0 : CPLFree(pszSRS);
431 0 : GDALClose(hDS);
432 0 : GDALExit(1);
433 : }
434 5 : CPLFree(pszSRS);
435 : }
436 8 : if (sOptions.bGeoTransform)
437 : {
438 5 : if (sOptions.nPixels == 0)
439 : {
440 0 : fprintf(stderr,
441 : "-outsize must be specified when -a_ullr is used.\n");
442 0 : GDALClose(hDS);
443 0 : GDALExit(1);
444 : }
445 5 : if (GDALDataset::FromHandle(hDS)->SetGeoTransform(gt) != CE_None)
446 : {
447 0 : GDALClose(hDS);
448 0 : GDALExit(1);
449 : }
450 : }
451 3 : else if (poInputDS && poInputDS->GetGCPCount() > 0)
452 : {
453 2 : GDALDataset::FromHandle(hDS)->SetGCPs(poInputDS->GetGCPCount(),
454 1 : poInputDS->GetGCPs(),
455 1 : poInputDS->GetGCPSpatialRef());
456 : }
457 :
458 8 : if (!sOptions.aosMetadata.empty())
459 : {
460 3 : GDALSetMetadata(hDS, sOptions.aosMetadata.List(), nullptr);
461 : }
462 8 : const int nBands = GDALGetRasterCount(hDS);
463 8 : if (sOptions.bSetNoData)
464 : {
465 15 : for (int i = 0; i < nBands; i++)
466 : {
467 11 : auto hBand = GDALGetRasterBand(hDS, i + 1);
468 11 : if (sOptions.eDT == GDT_Int64)
469 : {
470 0 : GDALSetRasterNoDataValueAsInt64(
471 0 : hBand, static_cast<int64_t>(std::strtoll(
472 : sOptions.osNoData.c_str(), nullptr, 10)));
473 : }
474 11 : else if (sOptions.eDT == GDT_UInt64)
475 : {
476 0 : GDALSetRasterNoDataValueAsUInt64(
477 0 : hBand, static_cast<uint64_t>(std::strtoull(
478 : sOptions.osNoData.c_str(), nullptr, 10)));
479 : }
480 : else
481 : {
482 11 : GDALSetRasterNoDataValue(hBand,
483 : CPLAtofM(sOptions.osNoData.c_str()));
484 : }
485 : }
486 : }
487 8 : if (!sOptions.adfBurnValues.empty())
488 : {
489 12 : for (int i = 0; i < nBands; i++)
490 : {
491 9 : GDALFillRaster(GDALGetRasterBand(hDS, i + 1),
492 9 : i < static_cast<int>(sOptions.adfBurnValues.size())
493 6 : ? sOptions.adfBurnValues[i]
494 3 : : sOptions.adfBurnValues.back(),
495 : 0);
496 : }
497 : }
498 :
499 8 : bool bHasGotErr = false;
500 8 : if (!bHasCreate)
501 : {
502 2 : GDALDatasetH hOutDS = GDALCreateCopy(
503 : hDriver, sOptions.osOutputFilename.c_str(), hDS, false,
504 1 : sOptions.aosCreateOptions.List(),
505 1 : sOptions.bQuiet ? GDALDummyProgress : GDALTermProgress, nullptr);
506 1 : if (hOutDS == nullptr)
507 : {
508 0 : GDALClose(hDS);
509 0 : GDALExit(1);
510 : }
511 1 : if (GDALClose(hOutDS) != CE_None)
512 : {
513 0 : bHasGotErr = true;
514 : }
515 : }
516 :
517 8 : const bool bWasFailureBefore = (CPLGetLastErrorType() == CE_Failure);
518 8 : if (GDALClose(hDS) != CE_None)
519 0 : bHasGotErr = true;
520 8 : if (!bWasFailureBefore && CPLGetLastErrorType() == CE_Failure)
521 : {
522 0 : bHasGotErr = true;
523 : }
524 :
525 8 : return bHasGotErr ? 1 : 0;
526 : }
527 :
528 0 : MAIN_END
|