Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Rasterize OGR shapes into a GDAL raster.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2015, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gdal_utils.h"
16 : #include "gdal_utils_priv.h"
17 :
18 : #include <cmath>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 : #include <algorithm>
23 : #include <limits>
24 : #include <vector>
25 :
26 : #include "commonutils.h"
27 : #include "cpl_conv.h"
28 : #include "cpl_error.h"
29 : #include "cpl_progress.h"
30 : #include "cpl_string.h"
31 : #include "gdal.h"
32 : #include "gdal_alg.h"
33 : #include "gdal_priv.h"
34 : #include "ogr_api.h"
35 : #include "ogr_core.h"
36 : #include "ogr_srs_api.h"
37 : #include "gdalargumentparser.h"
38 :
39 : /************************************************************************/
40 : /* GDALRasterizeOptions() */
41 : /************************************************************************/
42 :
43 : struct GDALRasterizeOptions
44 : {
45 : std::vector<int> anBandList{};
46 : std::vector<double> adfBurnValues{};
47 : bool bInverse = false;
48 : std::string osFormat{};
49 : bool b3D = false;
50 : GDALProgressFunc pfnProgress = GDALDummyProgress;
51 : void *pProgressData = nullptr;
52 : std::vector<std::string> aosLayers{};
53 : std::string osSQL{};
54 : std::string osDialect{};
55 : std::string osBurnAttribute{};
56 : std::string osWHERE{};
57 : CPLStringList aosRasterizeOptions{};
58 : CPLStringList aosTO{};
59 : double dfXRes = 0;
60 : double dfYRes = 0;
61 : CPLStringList aosCreationOptions{};
62 : GDALDataType eOutputType = GDT_Unknown;
63 : std::vector<double> adfInitVals{};
64 : std::string osNoData{};
65 : OGREnvelope sEnvelop{};
66 : int nXSize = 0;
67 : int nYSize = 0;
68 : OGRSpatialReference oOutputSRS{};
69 :
70 : bool bTargetAlignedPixels = false;
71 : bool bCreateOutput = false;
72 : };
73 :
74 : /************************************************************************/
75 : /* GDALRasterizeOptionsGetParser() */
76 : /************************************************************************/
77 :
78 : static std::unique_ptr<GDALArgumentParser>
79 67 : GDALRasterizeOptionsGetParser(GDALRasterizeOptions *psOptions,
80 : GDALRasterizeOptionsForBinary *psOptionsForBinary)
81 : {
82 : auto argParser = std::make_unique<GDALArgumentParser>(
83 67 : "gdal_rasterize", /* bForBinary=*/psOptionsForBinary != nullptr);
84 :
85 67 : argParser->add_description(_("Burns vector geometries into a raster."));
86 :
87 67 : argParser->add_epilog(
88 : _("This program burns vector geometries (points, lines, and polygons) "
89 67 : "into the raster band(s) of a raster image."));
90 :
91 : // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
92 67 : argParser->add_argument("-b")
93 134 : .metavar("<band>")
94 67 : .append()
95 67 : .scan<'i', int>()
96 : //.nargs(argparse::nargs_pattern::at_least_one)
97 67 : .help(_("The band(s) to burn values into."));
98 :
99 67 : argParser->add_argument("-i")
100 67 : .flag()
101 67 : .store_into(psOptions->bInverse)
102 67 : .help(_("Invert rasterization."));
103 :
104 67 : argParser->add_argument("-at")
105 67 : .flag()
106 : .action(
107 32 : [psOptions](const std::string &) {
108 : psOptions->aosRasterizeOptions.SetNameValue("ALL_TOUCHED",
109 32 : "TRUE");
110 67 : })
111 67 : .help(_("Enables the ALL_TOUCHED rasterization option."));
112 :
113 : // Mutually exclusive options: -burn, -3d, -a
114 : {
115 : // Required if options for binary
116 67 : auto &group = argParser->add_mutually_exclusive_group(
117 67 : psOptionsForBinary != nullptr);
118 :
119 : // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
120 67 : group.add_argument("-burn")
121 134 : .metavar("<value>")
122 67 : .scan<'g', double>()
123 67 : .append()
124 : //.nargs(argparse::nargs_pattern::at_least_one)
125 67 : .help(_("A fixed value to burn into the raster band(s)."));
126 :
127 67 : group.add_argument("-a")
128 134 : .metavar("<attribute_name>")
129 67 : .store_into(psOptions->osBurnAttribute)
130 : .help(_("Name of the field in the input layer to get the burn "
131 67 : "values from."));
132 :
133 67 : group.add_argument("-3d")
134 67 : .flag()
135 67 : .store_into(psOptions->b3D)
136 : .action(
137 5 : [psOptions](const std::string &) {
138 : psOptions->aosRasterizeOptions.SetNameValue(
139 5 : "BURN_VALUE_FROM", "Z");
140 67 : })
141 : .help(_("Indicates that a burn value should be extracted from the "
142 67 : "\"Z\" values of the feature."));
143 : }
144 :
145 67 : argParser->add_argument("-add")
146 67 : .flag()
147 : .action(
148 1 : [psOptions](const std::string &) {
149 1 : psOptions->aosRasterizeOptions.SetNameValue("MERGE_ALG", "ADD");
150 67 : })
151 : .help(_("Instead of burning a new value, this adds the new value to "
152 67 : "the existing raster."));
153 :
154 : // Undocumented
155 67 : argParser->add_argument("-chunkysize")
156 67 : .flag()
157 67 : .hidden()
158 : .action(
159 0 : [psOptions](const std::string &s) {
160 : psOptions->aosRasterizeOptions.SetNameValue("CHUNKYSIZE",
161 0 : s.c_str());
162 67 : });
163 :
164 : // Mutually exclusive -l, -sql
165 : {
166 67 : auto &group = argParser->add_mutually_exclusive_group(false);
167 :
168 67 : group.add_argument("-l")
169 134 : .metavar("<layer_name>")
170 67 : .append()
171 67 : .store_into(psOptions->aosLayers)
172 67 : .help(_("Name of the layer(s) to process."));
173 :
174 67 : group.add_argument("-sql")
175 134 : .metavar("<sql_statement>")
176 67 : .store_into(psOptions->osSQL)
177 : .action(
178 10 : [psOptions](const std::string &sql)
179 : {
180 9 : GByte *pabyRet = nullptr;
181 10 : if (!sql.empty() && sql.at(0) == '@' &&
182 10 : VSIIngestFile(nullptr, sql.substr(1).c_str(), &pabyRet,
183 : nullptr, 10 * 1024 * 1024))
184 : {
185 1 : GDALRemoveBOM(pabyRet);
186 1 : char *pszSQLStatement =
187 : reinterpret_cast<char *>(pabyRet);
188 : psOptions->osSQL =
189 1 : CPLRemoveSQLComments(pszSQLStatement);
190 1 : VSIFree(pszSQLStatement);
191 : }
192 76 : })
193 : .help(
194 : _("An SQL statement to be evaluated against the datasource to "
195 67 : "produce a virtual layer of features to be burned in."));
196 : }
197 :
198 67 : argParser->add_argument("-where")
199 134 : .metavar("<expression>")
200 67 : .store_into(psOptions->osWHERE)
201 : .help(_("An optional SQL WHERE style query expression to be applied to "
202 : "select features "
203 67 : "to burn in from the input layer(s)."));
204 :
205 67 : argParser->add_argument("-dialect")
206 134 : .metavar("<sql_dialect>")
207 67 : .store_into(psOptions->osDialect)
208 67 : .help(_("The SQL dialect to use for the SQL expression."));
209 :
210 : // Store later
211 67 : argParser->add_argument("-a_nodata")
212 134 : .metavar("<value>")
213 67 : .help(_("Assign a specified nodata value to output bands."));
214 :
215 : // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
216 67 : argParser->add_argument("-init")
217 134 : .metavar("<value>")
218 67 : .append()
219 : //.nargs(argparse::nargs_pattern::at_least_one)
220 67 : .scan<'g', double>()
221 67 : .help(_("Initialize the output bands to the specified value."));
222 :
223 67 : argParser->add_argument("-a_srs")
224 134 : .metavar("<srs_def>")
225 : .action(
226 4 : [psOptions](const std::string &osOutputSRSDef)
227 : {
228 2 : if (psOptions->oOutputSRS.SetFromUserInput(
229 2 : osOutputSRSDef.c_str()) != OGRERR_NONE)
230 : {
231 : throw std::invalid_argument(
232 0 : std::string("Failed to process SRS definition: ")
233 0 : .append(osOutputSRSDef));
234 : }
235 2 : psOptions->bCreateOutput = true;
236 69 : })
237 67 : .help(_("The spatial reference system to use for the output raster."));
238 :
239 67 : argParser->add_argument("-to")
240 134 : .metavar("<NAME>=<VALUE>")
241 67 : .append()
242 1 : .action([psOptions](const std::string &s)
243 68 : { psOptions->aosTO.AddString(s.c_str()); })
244 67 : .help(_("Set a transformer option."));
245 :
246 : // Store later
247 67 : argParser->add_argument("-te")
248 134 : .metavar("<xmin> <ymin> <xmax> <ymax>")
249 67 : .nargs(4)
250 67 : .scan<'g', double>()
251 67 : .help(_("Set georeferenced extents of output file to be created."));
252 :
253 : // Mutex with tr
254 : {
255 67 : auto &group = argParser->add_mutually_exclusive_group(false);
256 :
257 : // Store later
258 67 : group.add_argument("-tr")
259 134 : .metavar("<xres> <yres>")
260 67 : .nargs(2)
261 67 : .scan<'g', double>()
262 : .help(
263 67 : _("Set output file resolution in target georeferenced units."));
264 :
265 : // Store later
266 : // Note: this is supposed to be int but for backward compatibility, we
267 : // use double
268 67 : auto &arg = group.add_argument("-ts")
269 134 : .metavar("<width> <height>")
270 67 : .nargs(2)
271 67 : .scan<'g', double>()
272 67 : .help(_("Set output file size in pixels and lines."));
273 :
274 67 : argParser->add_hidden_alias_for(arg, "-outsize");
275 : }
276 :
277 67 : argParser->add_argument("-tap")
278 67 : .flag()
279 67 : .store_into(psOptions->bTargetAlignedPixels)
280 1 : .action([psOptions](const std::string &)
281 67 : { psOptions->bCreateOutput = true; })
282 : .help(_("Align the coordinates of the extent to the values of the "
283 67 : "output raster."));
284 :
285 67 : argParser->add_argument("-optim")
286 134 : .metavar("AUTO|VECTOR|RASTER")
287 : .action(
288 36 : [psOptions](const std::string &s) {
289 36 : psOptions->aosRasterizeOptions.SetNameValue("OPTIM", s.c_str());
290 67 : })
291 67 : .help(_("Force the algorithm used."));
292 :
293 67 : argParser->add_creation_options_argument(psOptions->aosCreationOptions)
294 2 : .action([psOptions](const std::string &)
295 67 : { psOptions->bCreateOutput = true; });
296 :
297 67 : argParser->add_output_type_argument(psOptions->eOutputType)
298 2 : .action([psOptions](const std::string &)
299 67 : { psOptions->bCreateOutput = true; });
300 :
301 67 : argParser->add_output_format_argument(psOptions->osFormat)
302 12 : .action([psOptions](const std::string &)
303 67 : { psOptions->bCreateOutput = true; });
304 :
305 : // Written that way so that in library mode, users can still use the -q
306 : // switch, even if it has no effect
307 : argParser->add_quiet_argument(
308 67 : psOptionsForBinary ? &(psOptionsForBinary->bQuiet) : nullptr);
309 :
310 67 : if (psOptionsForBinary)
311 : {
312 :
313 : argParser->add_open_options_argument(
314 11 : psOptionsForBinary->aosOpenOptions);
315 :
316 11 : argParser->add_argument("src_datasource")
317 22 : .metavar("<src_datasource>")
318 11 : .store_into(psOptionsForBinary->osSource)
319 11 : .help(_("Any vector supported readable datasource."));
320 :
321 11 : argParser->add_argument("dst_filename")
322 22 : .metavar("<dst_filename>")
323 11 : .store_into(psOptionsForBinary->osDest)
324 11 : .help(_("The GDAL raster supported output file."));
325 : }
326 :
327 67 : return argParser;
328 : }
329 :
330 : /************************************************************************/
331 : /* GDALRasterizeAppGetParserUsage() */
332 : /************************************************************************/
333 :
334 0 : std::string GDALRasterizeAppGetParserUsage()
335 : {
336 : try
337 : {
338 0 : GDALRasterizeOptions sOptions;
339 0 : GDALRasterizeOptionsForBinary sOptionsForBinary;
340 : auto argParser =
341 0 : GDALRasterizeOptionsGetParser(&sOptions, &sOptionsForBinary);
342 0 : return argParser->usage();
343 : }
344 0 : catch (const std::exception &err)
345 : {
346 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
347 0 : err.what());
348 0 : return std::string();
349 : }
350 : }
351 :
352 : /************************************************************************/
353 : /* InvertGeometries() */
354 : /************************************************************************/
355 :
356 3 : static void InvertGeometries(GDALDatasetH hDstDS,
357 : std::vector<OGRGeometryH> &ahGeometries)
358 :
359 : {
360 3 : OGRMultiPolygon *poInvertMP = new OGRMultiPolygon();
361 :
362 : /* -------------------------------------------------------------------- */
363 : /* Create a ring that is a bit outside the raster dataset. */
364 : /* -------------------------------------------------------------------- */
365 3 : const int brx = GDALGetRasterXSize(hDstDS) + 2;
366 3 : const int bry = GDALGetRasterYSize(hDstDS) + 2;
367 :
368 3 : double adfGeoTransform[6] = {};
369 3 : GDALGetGeoTransform(hDstDS, adfGeoTransform);
370 :
371 3 : auto poUniverseRing = std::make_unique<OGRLinearRing>();
372 :
373 3 : poUniverseRing->addPoint(
374 3 : adfGeoTransform[0] + -2 * adfGeoTransform[1] + -2 * adfGeoTransform[2],
375 3 : adfGeoTransform[3] + -2 * adfGeoTransform[4] + -2 * adfGeoTransform[5]);
376 :
377 3 : poUniverseRing->addPoint(adfGeoTransform[0] + brx * adfGeoTransform[1] +
378 3 : -2 * adfGeoTransform[2],
379 3 : adfGeoTransform[3] + brx * adfGeoTransform[4] +
380 3 : -2 * adfGeoTransform[5]);
381 :
382 3 : poUniverseRing->addPoint(adfGeoTransform[0] + brx * adfGeoTransform[1] +
383 3 : bry * adfGeoTransform[2],
384 3 : adfGeoTransform[3] + brx * adfGeoTransform[4] +
385 3 : bry * adfGeoTransform[5]);
386 :
387 3 : poUniverseRing->addPoint(adfGeoTransform[0] + -2 * adfGeoTransform[1] +
388 3 : bry * adfGeoTransform[2],
389 3 : adfGeoTransform[3] + -2 * adfGeoTransform[4] +
390 3 : bry * adfGeoTransform[5]);
391 :
392 3 : poUniverseRing->addPoint(
393 3 : adfGeoTransform[0] + -2 * adfGeoTransform[1] + -2 * adfGeoTransform[2],
394 3 : adfGeoTransform[3] + -2 * adfGeoTransform[4] + -2 * adfGeoTransform[5]);
395 :
396 3 : auto poUniversePoly = std::make_unique<OGRPolygon>();
397 3 : poUniversePoly->addRing(std::move(poUniverseRing));
398 3 : poInvertMP->addGeometry(std::move(poUniversePoly));
399 :
400 3 : bool bFoundNonPoly = false;
401 : // If we have GEOS, use it to "subtract" each polygon from the universe
402 : // multipolygon
403 3 : if (OGRGeometryFactory::haveGEOS())
404 : {
405 3 : OGRGeometry *poInvertMPAsGeom = poInvertMP;
406 3 : poInvertMP = nullptr;
407 3 : CPL_IGNORE_RET_VAL(poInvertMP);
408 10 : for (unsigned int iGeom = 0; iGeom < ahGeometries.size(); iGeom++)
409 : {
410 7 : auto poGeom = OGRGeometry::FromHandle(ahGeometries[iGeom]);
411 7 : const auto eGType = OGR_GT_Flatten(poGeom->getGeometryType());
412 7 : if (eGType != wkbPolygon && eGType != wkbMultiPolygon)
413 : {
414 1 : if (!bFoundNonPoly)
415 : {
416 1 : bFoundNonPoly = true;
417 1 : CPLError(CE_Warning, CPLE_AppDefined,
418 : "Ignoring non-polygon geometries in -i mode");
419 : }
420 : }
421 : else
422 : {
423 6 : auto poNewGeom = poInvertMPAsGeom->Difference(poGeom);
424 6 : if (poNewGeom)
425 : {
426 6 : delete poInvertMPAsGeom;
427 6 : poInvertMPAsGeom = poNewGeom;
428 : }
429 : }
430 :
431 7 : delete poGeom;
432 : }
433 :
434 3 : ahGeometries.resize(1);
435 3 : ahGeometries[0] = OGRGeometry::ToHandle(poInvertMPAsGeom);
436 3 : return;
437 : }
438 :
439 : OGRPolygon &hUniversePoly =
440 0 : *poInvertMP->getGeometryRef(poInvertMP->getNumGeometries() - 1);
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* If we don't have GEOS, add outer rings of polygons as inner */
444 : /* rings of poUniversePoly and inner rings as sub-polygons. Note */
445 : /* that this only works properly if the polygons are disjoint, in */
446 : /* the sense that the outer ring of any polygon is not inside the */
447 : /* outer ring of another one. So the scenario of */
448 : /* https://github.com/OSGeo/gdal/issues/8689 with an "island" in */
449 : /* the middle of a hole will not work properly. */
450 : /* -------------------------------------------------------------------- */
451 0 : for (unsigned int iGeom = 0; iGeom < ahGeometries.size(); iGeom++)
452 : {
453 : const auto eGType =
454 0 : OGR_GT_Flatten(OGR_G_GetGeometryType(ahGeometries[iGeom]));
455 0 : if (eGType != wkbPolygon && eGType != wkbMultiPolygon)
456 : {
457 0 : if (!bFoundNonPoly)
458 : {
459 0 : bFoundNonPoly = true;
460 0 : CPLError(CE_Warning, CPLE_AppDefined,
461 : "Ignoring non-polygon geometries in -i mode");
462 : }
463 0 : OGR_G_DestroyGeometry(ahGeometries[iGeom]);
464 0 : continue;
465 : }
466 :
467 : const auto ProcessPoly =
468 0 : [&hUniversePoly, poInvertMP](OGRPolygon *poPoly)
469 : {
470 0 : for (int i = poPoly->getNumInteriorRings() - 1; i >= 0; --i)
471 : {
472 0 : auto poNewPoly = std::make_unique<OGRPolygon>();
473 : std::unique_ptr<OGRLinearRing> poRing(
474 0 : poPoly->stealInteriorRing(i));
475 0 : poNewPoly->addRing(std::move(poRing));
476 0 : poInvertMP->addGeometry(std::move(poNewPoly));
477 : }
478 0 : std::unique_ptr<OGRLinearRing> poShell(poPoly->stealExteriorRing());
479 0 : hUniversePoly.addRing(std::move(poShell));
480 0 : };
481 :
482 0 : if (eGType == wkbPolygon)
483 : {
484 : auto poPoly =
485 0 : OGRGeometry::FromHandle(ahGeometries[iGeom])->toPolygon();
486 0 : ProcessPoly(poPoly);
487 0 : delete poPoly;
488 : }
489 : else
490 : {
491 : auto poMulti =
492 0 : OGRGeometry::FromHandle(ahGeometries[iGeom])->toMultiPolygon();
493 0 : for (auto *poPoly : *poMulti)
494 : {
495 0 : ProcessPoly(poPoly);
496 : }
497 0 : delete poMulti;
498 : }
499 : }
500 :
501 0 : ahGeometries.resize(1);
502 0 : ahGeometries[0] = OGRGeometry::ToHandle(poInvertMP);
503 : }
504 :
505 : /************************************************************************/
506 : /* ProcessLayer() */
507 : /* */
508 : /* Process all the features in a layer selection, collecting */
509 : /* geometries and burn values. */
510 : /************************************************************************/
511 :
512 55 : static CPLErr ProcessLayer(OGRLayerH hSrcLayer, bool bSRSIsSet,
513 : GDALDataset *poDstDS,
514 : const std::vector<int> &anBandList,
515 : const std::vector<double> &adfBurnValues, bool b3D,
516 : bool bInverse, const std::string &osBurnAttribute,
517 : CSLConstList papszRasterizeOptions,
518 : CSLConstList papszTO, GDALProgressFunc pfnProgress,
519 : void *pProgressData)
520 :
521 : {
522 55 : GDALDatasetH hDstDS = GDALDataset::ToHandle(poDstDS);
523 :
524 : /* -------------------------------------------------------------------- */
525 : /* Checkout that SRS are the same. */
526 : /* If -a_srs is specified, skip the test */
527 : /* -------------------------------------------------------------------- */
528 55 : OGRCoordinateTransformationH hCT = nullptr;
529 55 : if (!bSRSIsSet)
530 : {
531 53 : OGRSpatialReferenceH hDstSRS = GDALGetSpatialRef(hDstDS);
532 :
533 53 : if (hDstSRS)
534 14 : hDstSRS = OSRClone(hDstSRS);
535 39 : else if (GDALGetMetadata(hDstDS, "RPC") != nullptr)
536 : {
537 2 : hDstSRS = OSRNewSpatialReference(nullptr);
538 2 : CPL_IGNORE_RET_VAL(
539 2 : OSRSetFromUserInput(hDstSRS, SRS_WKT_WGS84_LAT_LONG));
540 2 : OSRSetAxisMappingStrategy(hDstSRS, OAMS_TRADITIONAL_GIS_ORDER);
541 : }
542 :
543 53 : OGRSpatialReferenceH hSrcSRS = OGR_L_GetSpatialRef(hSrcLayer);
544 53 : if (hDstSRS != nullptr && hSrcSRS != nullptr)
545 : {
546 16 : if (OSRIsSame(hSrcSRS, hDstSRS) == FALSE)
547 : {
548 1 : hCT = OCTNewCoordinateTransformation(hSrcSRS, hDstSRS);
549 1 : if (hCT == nullptr)
550 : {
551 0 : CPLError(CE_Warning, CPLE_AppDefined,
552 : "The output raster dataset and the input vector "
553 : "layer do not have the same SRS.\n"
554 : "And reprojection of input data did not work. "
555 : "Results might be incorrect.");
556 : }
557 : }
558 : }
559 37 : else if (hDstSRS != nullptr && hSrcSRS == nullptr)
560 : {
561 0 : CPLError(CE_Warning, CPLE_AppDefined,
562 : "The output raster dataset has a SRS, but the input "
563 : "vector layer SRS is unknown.\n"
564 : "Ensure input vector has the same SRS, otherwise results "
565 : "might be incorrect.");
566 : }
567 37 : else if (hDstSRS == nullptr && hSrcSRS != nullptr)
568 : {
569 1 : CPLError(CE_Warning, CPLE_AppDefined,
570 : "The input vector layer has a SRS, but the output raster "
571 : "dataset SRS is unknown.\n"
572 : "Ensure output raster dataset has the same SRS, otherwise "
573 : "results might be incorrect.");
574 : }
575 :
576 53 : if (hDstSRS != nullptr)
577 : {
578 16 : OSRDestroySpatialReference(hDstSRS);
579 : }
580 : }
581 :
582 : /* -------------------------------------------------------------------- */
583 : /* Get field index, and check. */
584 : /* -------------------------------------------------------------------- */
585 55 : int iBurnField = -1;
586 55 : bool bUseInt64 = false;
587 55 : if (!osBurnAttribute.empty())
588 : {
589 5 : OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hSrcLayer);
590 5 : iBurnField = OGR_FD_GetFieldIndex(hLayerDefn, osBurnAttribute.c_str());
591 5 : if (iBurnField == -1)
592 : {
593 1 : CPLError(CE_Failure, CPLE_AppDefined,
594 : "Failed to find field %s on layer %s.",
595 : osBurnAttribute.c_str(),
596 : OGR_FD_GetName(OGR_L_GetLayerDefn(hSrcLayer)));
597 1 : if (hCT != nullptr)
598 0 : OCTDestroyCoordinateTransformation(hCT);
599 1 : return CE_Failure;
600 : }
601 4 : if (OGR_Fld_GetType(OGR_FD_GetFieldDefn(hLayerDefn, iBurnField)) ==
602 : OFTInteger64)
603 : {
604 1 : GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, anBandList[0]);
605 1 : if (hBand && GDALGetRasterDataType(hBand) == GDT_Int64)
606 : {
607 1 : bUseInt64 = true;
608 : }
609 : }
610 : }
611 :
612 : /* -------------------------------------------------------------------- */
613 : /* Collect the geometries from this layer, and build list of */
614 : /* burn values. */
615 : /* -------------------------------------------------------------------- */
616 54 : OGRFeatureH hFeat = nullptr;
617 108 : std::vector<OGRGeometryH> ahGeometries;
618 108 : std::vector<double> adfFullBurnValues;
619 54 : std::vector<int64_t> anFullBurnValues;
620 :
621 54 : OGR_L_ResetReading(hSrcLayer);
622 :
623 2057 : while ((hFeat = OGR_L_GetNextFeature(hSrcLayer)) != nullptr)
624 : {
625 2003 : OGRGeometryH hGeom = OGR_F_StealGeometry(hFeat);
626 2003 : if (hGeom == nullptr)
627 : {
628 5 : OGR_F_Destroy(hFeat);
629 5 : continue;
630 : }
631 :
632 1998 : if (hCT != nullptr)
633 : {
634 1 : if (OGR_G_Transform(hGeom, hCT) != OGRERR_NONE)
635 : {
636 0 : OGR_F_Destroy(hFeat);
637 0 : OGR_G_DestroyGeometry(hGeom);
638 0 : continue;
639 : }
640 : }
641 1998 : ahGeometries.push_back(hGeom);
642 :
643 4152 : for (unsigned int iBand = 0; iBand < anBandList.size(); iBand++)
644 : {
645 2154 : if (!adfBurnValues.empty())
646 275 : adfFullBurnValues.push_back(adfBurnValues[std::min(
647 : iBand,
648 550 : static_cast<unsigned int>(adfBurnValues.size()) - 1)]);
649 1879 : else if (!osBurnAttribute.empty())
650 : {
651 16 : if (bUseInt64)
652 1 : anFullBurnValues.push_back(
653 1 : OGR_F_GetFieldAsInteger64(hFeat, iBurnField));
654 : else
655 15 : adfFullBurnValues.push_back(
656 15 : OGR_F_GetFieldAsDouble(hFeat, iBurnField));
657 : }
658 1863 : else if (b3D)
659 : {
660 : /* Points and Lines will have their "z" values collected at the
661 : point and line levels respectively. Not implemented for
662 : polygons */
663 1863 : adfFullBurnValues.push_back(0.0);
664 : }
665 : }
666 :
667 1998 : OGR_F_Destroy(hFeat);
668 : }
669 :
670 54 : if (hCT != nullptr)
671 1 : OCTDestroyCoordinateTransformation(hCT);
672 :
673 : /* -------------------------------------------------------------------- */
674 : /* If we are in inverse mode, we add one extra ring around the */
675 : /* whole dataset to invert the concept of insideness and then */
676 : /* merge everything into one geometry collection. */
677 : /* -------------------------------------------------------------------- */
678 54 : if (bInverse)
679 : {
680 3 : if (ahGeometries.empty())
681 : {
682 0 : for (unsigned int iBand = 0; iBand < anBandList.size(); iBand++)
683 : {
684 0 : if (!adfBurnValues.empty())
685 0 : adfFullBurnValues.push_back(adfBurnValues[std::min(
686 : iBand,
687 0 : static_cast<unsigned int>(adfBurnValues.size()) - 1)]);
688 : else /* FIXME? Not sure what to do exactly in the else case, but
689 : we must insert a value */
690 : {
691 0 : adfFullBurnValues.push_back(0.0);
692 0 : anFullBurnValues.push_back(0);
693 : }
694 : }
695 : }
696 :
697 3 : InvertGeometries(hDstDS, ahGeometries);
698 : }
699 :
700 : /* -------------------------------------------------------------------- */
701 : /* If we have transformer options, create the transformer here */
702 : /* Coordinate transformation to the target SRS has already been */
703 : /* done, so we just need to convert to target raster space. */
704 : /* Note: this is somewhat identical to what is done in */
705 : /* GDALRasterizeGeometries() itself, except we can pass transformer*/
706 : /* options. */
707 : /* -------------------------------------------------------------------- */
708 :
709 54 : void *pTransformArg = nullptr;
710 54 : GDALTransformerFunc pfnTransformer = nullptr;
711 54 : CPLErr eErr = CE_None;
712 54 : if (papszTO != nullptr)
713 : {
714 1 : GDALDataset *poDS = GDALDataset::FromHandle(hDstDS);
715 1 : char **papszTransformerOptions = CSLDuplicate(papszTO);
716 1 : GDALGeoTransform gt;
717 2 : if (poDS->GetGeoTransform(gt) != CE_None && poDS->GetGCPCount() == 0 &&
718 1 : poDS->GetMetadata("RPC") == nullptr)
719 : {
720 0 : papszTransformerOptions = CSLSetNameValue(
721 : papszTransformerOptions, "DST_METHOD", "NO_GEOTRANSFORM");
722 : }
723 :
724 1 : pTransformArg = GDALCreateGenImgProjTransformer2(
725 : nullptr, hDstDS, papszTransformerOptions);
726 1 : CSLDestroy(papszTransformerOptions);
727 :
728 1 : pfnTransformer = GDALGenImgProjTransform;
729 1 : if (pTransformArg == nullptr)
730 : {
731 0 : eErr = CE_Failure;
732 : }
733 : }
734 :
735 : /* -------------------------------------------------------------------- */
736 : /* Perform the burn. */
737 : /* -------------------------------------------------------------------- */
738 54 : if (eErr == CE_None)
739 : {
740 54 : if (bUseInt64)
741 : {
742 2 : eErr = GDALRasterizeGeometriesInt64(
743 1 : hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
744 1 : static_cast<int>(ahGeometries.size()), ahGeometries.data(),
745 1 : pfnTransformer, pTransformArg, anFullBurnValues.data(),
746 : papszRasterizeOptions, pfnProgress, pProgressData);
747 : }
748 : else
749 : {
750 106 : eErr = GDALRasterizeGeometries(
751 53 : hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
752 53 : static_cast<int>(ahGeometries.size()), ahGeometries.data(),
753 53 : pfnTransformer, pTransformArg, adfFullBurnValues.data(),
754 : papszRasterizeOptions, pfnProgress, pProgressData);
755 : }
756 : }
757 :
758 : /* -------------------------------------------------------------------- */
759 : /* Cleanup */
760 : /* -------------------------------------------------------------------- */
761 :
762 54 : if (pTransformArg)
763 1 : GDALDestroyTransformer(pTransformArg);
764 :
765 2048 : for (int iGeom = static_cast<int>(ahGeometries.size()) - 1; iGeom >= 0;
766 : iGeom--)
767 1994 : OGR_G_DestroyGeometry(ahGeometries[iGeom]);
768 :
769 54 : return eErr;
770 : }
771 :
772 : /************************************************************************/
773 : /* CreateOutputDataset() */
774 : /************************************************************************/
775 :
776 30 : static std::unique_ptr<GDALDataset> CreateOutputDataset(
777 : const std::vector<OGRLayerH> &ahLayers, OGRSpatialReferenceH hSRS,
778 : OGREnvelope sEnvelop, GDALDriverH hDriver, const char *pszDest, int nXSize,
779 : int nYSize, double dfXRes, double dfYRes, bool bTargetAlignedPixels,
780 : int nBandCount, GDALDataType eOutputType, CSLConstList papszCreationOptions,
781 : const std::vector<double> &adfInitVals, const char *pszNoData)
782 : {
783 30 : bool bFirstLayer = true;
784 30 : const bool bBoundsSpecifiedByUser = sEnvelop.IsInit();
785 :
786 59 : for (unsigned int i = 0; i < ahLayers.size(); i++)
787 : {
788 30 : OGRLayerH hLayer = ahLayers[i];
789 :
790 30 : if (!bBoundsSpecifiedByUser)
791 : {
792 27 : OGREnvelope sLayerEnvelop;
793 :
794 27 : if (OGR_L_GetExtent(hLayer, &sLayerEnvelop, TRUE) != OGRERR_NONE)
795 : {
796 1 : CPLError(CE_Failure, CPLE_AppDefined,
797 : "Cannot get layer extent");
798 1 : return nullptr;
799 : }
800 :
801 : /* Voluntarily increase the extent by a half-pixel size to avoid */
802 : /* missing points on the border */
803 26 : if (!bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
804 : {
805 9 : sLayerEnvelop.MinX -= dfXRes / 2;
806 9 : sLayerEnvelop.MaxX += dfXRes / 2;
807 9 : sLayerEnvelop.MinY -= dfYRes / 2;
808 9 : sLayerEnvelop.MaxY += dfYRes / 2;
809 : }
810 :
811 26 : sEnvelop.Merge(sLayerEnvelop);
812 : }
813 :
814 29 : if (bFirstLayer)
815 : {
816 29 : if (hSRS == nullptr)
817 27 : hSRS = OGR_L_GetSpatialRef(hLayer);
818 :
819 29 : bFirstLayer = false;
820 : }
821 : }
822 :
823 29 : if (!sEnvelop.IsInit())
824 : {
825 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
826 0 : return nullptr;
827 : }
828 :
829 29 : if (dfXRes == 0 && dfYRes == 0)
830 : {
831 16 : if (nXSize == 0 || nYSize == 0)
832 : {
833 1 : CPLError(CE_Failure, CPLE_AppDefined,
834 : "Size and resolution are missing");
835 1 : return nullptr;
836 : }
837 15 : dfXRes = (sEnvelop.MaxX - sEnvelop.MinX) / nXSize;
838 15 : dfYRes = (sEnvelop.MaxY - sEnvelop.MinY) / nYSize;
839 : }
840 13 : else if (bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
841 : {
842 1 : sEnvelop.MinX = floor(sEnvelop.MinX / dfXRes) * dfXRes;
843 1 : sEnvelop.MaxX = ceil(sEnvelop.MaxX / dfXRes) * dfXRes;
844 1 : sEnvelop.MinY = floor(sEnvelop.MinY / dfYRes) * dfYRes;
845 1 : sEnvelop.MaxY = ceil(sEnvelop.MaxY / dfYRes) * dfYRes;
846 : }
847 :
848 28 : if (dfXRes == 0 || dfYRes == 0)
849 : {
850 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
851 0 : return nullptr;
852 : }
853 :
854 28 : if (nXSize == 0 && nYSize == 0)
855 : {
856 : // coverity[divide_by_zero]
857 13 : const double dfXSize = 0.5 + (sEnvelop.MaxX - sEnvelop.MinX) / dfXRes;
858 : // coverity[divide_by_zero]
859 13 : const double dfYSize = 0.5 + (sEnvelop.MaxY - sEnvelop.MinY) / dfYRes;
860 13 : if (dfXSize > std::numeric_limits<int>::max() ||
861 12 : dfXSize < std::numeric_limits<int>::min() ||
862 36 : dfYSize > std::numeric_limits<int>::max() ||
863 11 : dfYSize < std::numeric_limits<int>::min())
864 : {
865 2 : CPLError(CE_Failure, CPLE_AppDefined,
866 : "Invalid computed output raster size: %f x %f", dfXSize,
867 : dfYSize);
868 2 : return nullptr;
869 : }
870 11 : nXSize = static_cast<int>(dfXSize);
871 11 : nYSize = static_cast<int>(dfYSize);
872 : }
873 :
874 : auto poDstDS =
875 : std::unique_ptr<GDALDataset>(GDALDriver::FromHandle(hDriver)->Create(
876 : pszDest, nXSize, nYSize, nBandCount, eOutputType,
877 52 : papszCreationOptions));
878 26 : if (poDstDS == nullptr)
879 : {
880 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszDest);
881 0 : return nullptr;
882 : }
883 :
884 : GDALGeoTransform gt = {sEnvelop.MinX, dfXRes, 0.0,
885 26 : sEnvelop.MaxY, 0.0, -dfYRes};
886 26 : poDstDS->SetGeoTransform(gt);
887 :
888 26 : if (hSRS)
889 7 : poDstDS->SetSpatialRef(OGRSpatialReference::FromHandle(hSRS));
890 :
891 26 : if (pszNoData)
892 : {
893 78 : for (int iBand = 0; iBand < nBandCount; iBand++)
894 : {
895 52 : auto poBand = poDstDS->GetRasterBand(iBand + 1);
896 52 : if (poBand->GetRasterDataType() == GDT_Int64)
897 1 : poBand->SetNoDataValueAsInt64(CPLAtoGIntBig(pszNoData));
898 : else
899 51 : poBand->SetNoDataValue(CPLAtof(pszNoData));
900 : }
901 : }
902 :
903 26 : if (!adfInitVals.empty())
904 : {
905 30 : for (int iBand = 0;
906 30 : iBand < std::min(nBandCount, static_cast<int>(adfInitVals.size()));
907 : iBand++)
908 : {
909 21 : auto poBand = poDstDS->GetRasterBand(iBand + 1);
910 21 : poBand->Fill(adfInitVals[iBand]);
911 : }
912 : }
913 :
914 26 : return poDstDS;
915 : }
916 :
917 : /************************************************************************/
918 : /* GDALRasterize() */
919 : /************************************************************************/
920 :
921 : /* clang-format off */
922 : /**
923 : * Burns vector geometries into a raster
924 : *
925 : * This is the equivalent of the
926 : * <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
927 : *
928 : * GDALRasterizeOptions* must be allocated and freed with
929 : * GDALRasterizeOptionsNew() and GDALRasterizeOptionsFree() respectively.
930 : * pszDest and hDstDS cannot be used at the same time.
931 : *
932 : * @param pszDest the destination dataset path or NULL.
933 : * @param hDstDS the destination dataset or NULL.
934 : * @param hSrcDataset the source dataset handle.
935 : * @param psOptionsIn the options struct returned by GDALRasterizeOptionsNew()
936 : * or NULL.
937 : * @param pbUsageError pointer to an integer output variable to store if any
938 : * usage error has occurred or NULL.
939 : * @return the output dataset (new dataset that must be closed using
940 : * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
941 : *
942 : * @since GDAL 2.1
943 : */
944 : /* clang-format on */
945 :
946 64 : GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS,
947 : GDALDatasetH hSrcDataset,
948 : const GDALRasterizeOptions *psOptionsIn,
949 : int *pbUsageError)
950 : {
951 64 : GDALDataset *poOutDS = GDALDataset::FromHandle(hDstDS);
952 : #define hDstDS no_longer_use_hDstDS
953 64 : if (pszDest == nullptr && poOutDS == nullptr)
954 : {
955 0 : CPLError(CE_Failure, CPLE_AppDefined,
956 : "pszDest == NULL && hDstDS == NULL");
957 :
958 0 : if (pbUsageError)
959 0 : *pbUsageError = TRUE;
960 0 : return nullptr;
961 : }
962 64 : if (hSrcDataset == nullptr)
963 : {
964 0 : CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
965 :
966 0 : if (pbUsageError)
967 0 : *pbUsageError = TRUE;
968 0 : return nullptr;
969 : }
970 64 : if (poOutDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
971 : {
972 0 : CPLError(CE_Failure, CPLE_AppDefined,
973 : "hDstDS != NULL but options that imply creating a new dataset "
974 : "have been set.");
975 :
976 0 : if (pbUsageError)
977 0 : *pbUsageError = TRUE;
978 0 : return nullptr;
979 : }
980 :
981 : std::unique_ptr<GDALRasterizeOptions, decltype(&GDALRasterizeOptionsFree)>
982 128 : psOptionsToFree(nullptr, GDALRasterizeOptionsFree);
983 128 : GDALRasterizeOptions sOptions;
984 64 : if (psOptionsIn)
985 64 : sOptions = *psOptionsIn;
986 :
987 64 : std::unique_ptr<GDALDataset> poNewOutDS;
988 64 : if (pszDest == nullptr)
989 13 : pszDest = poOutDS->GetDescription();
990 :
991 90 : if (sOptions.osSQL.empty() && sOptions.aosLayers.empty() &&
992 26 : GDALDatasetGetLayerCount(hSrcDataset) != 1)
993 : {
994 0 : CPLError(CE_Failure, CPLE_NotSupported,
995 : "Neither -sql nor -l are specified, but the source dataset "
996 : "has not one single layer.");
997 0 : if (pbUsageError)
998 0 : *pbUsageError = TRUE;
999 0 : return nullptr;
1000 : }
1001 :
1002 : /* -------------------------------------------------------------------- */
1003 : /* Open target raster file. Eventually we will add optional */
1004 : /* creation. */
1005 : /* -------------------------------------------------------------------- */
1006 64 : const bool bCreateOutput = sOptions.bCreateOutput || poOutDS == nullptr;
1007 :
1008 64 : GDALDriverH hDriver = nullptr;
1009 64 : if (bCreateOutput)
1010 : {
1011 35 : CPLString osFormat;
1012 35 : if (sOptions.osFormat.empty())
1013 : {
1014 23 : osFormat = GetOutputDriverForRaster(pszDest);
1015 23 : if (osFormat.empty())
1016 : {
1017 0 : return nullptr;
1018 : }
1019 : }
1020 : else
1021 : {
1022 12 : osFormat = sOptions.osFormat;
1023 : }
1024 :
1025 : /* --------------------------------------------------------------------
1026 : */
1027 : /* Find the output driver. */
1028 : /* --------------------------------------------------------------------
1029 : */
1030 35 : hDriver = GDALGetDriverByName(osFormat);
1031 : char **papszDriverMD =
1032 35 : hDriver ? GDALGetMetadata(hDriver, nullptr) : nullptr;
1033 35 : if (hDriver == nullptr)
1034 : {
1035 1 : CPLError(CE_Failure, CPLE_NotSupported,
1036 : "Output driver `%s' not recognised.", osFormat.c_str());
1037 1 : return nullptr;
1038 : }
1039 34 : if (!CPLTestBool(
1040 : CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER, "FALSE")))
1041 : {
1042 1 : CPLError(CE_Failure, CPLE_NotSupported,
1043 : "Output driver `%s' is not a raster driver.",
1044 : osFormat.c_str());
1045 1 : return nullptr;
1046 : }
1047 33 : if (!CPLTestBool(
1048 : CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
1049 : {
1050 1 : CPLError(CE_Failure, CPLE_NotSupported,
1051 : "Output driver `%s' does not support direct output file "
1052 : "creation. "
1053 : "To write a file to this format, first write to a "
1054 : "different format such as "
1055 : "GeoTIFF and then convert the output.",
1056 : osFormat.c_str());
1057 1 : return nullptr;
1058 : }
1059 : }
1060 :
1061 4 : auto calculateSize = [&](const OGREnvelope &sEnvelope) -> bool
1062 : {
1063 4 : const double width{sEnvelope.MaxX - sEnvelope.MinX};
1064 4 : if (std::isnan(width))
1065 : {
1066 0 : return false;
1067 : }
1068 :
1069 4 : const double height{sEnvelope.MaxY - sEnvelope.MinY};
1070 4 : if (std::isnan(height))
1071 : {
1072 0 : return false;
1073 : }
1074 :
1075 4 : if (height == 0 || width == 0)
1076 : {
1077 0 : return false;
1078 : }
1079 :
1080 4 : if (sOptions.nXSize == 0)
1081 : {
1082 2 : const double xSize{
1083 2 : (sEnvelope.MaxX - sEnvelope.MinX) /
1084 2 : ((sEnvelope.MaxY - sEnvelope.MinY) / sOptions.nYSize)};
1085 4 : if (std::isnan(xSize) || xSize > std::numeric_limits<int>::max() ||
1086 2 : xSize < std::numeric_limits<int>::min())
1087 : {
1088 0 : return false;
1089 : }
1090 2 : sOptions.nXSize = static_cast<int>(xSize);
1091 : }
1092 : else
1093 : {
1094 2 : const double ySize{
1095 2 : (sEnvelope.MaxY - sEnvelope.MinY) /
1096 2 : ((sEnvelope.MaxX - sEnvelope.MinX) / sOptions.nXSize)};
1097 4 : if (std::isnan(ySize) || ySize > std::numeric_limits<int>::max() ||
1098 2 : ySize < std::numeric_limits<int>::min())
1099 : {
1100 0 : return false;
1101 : }
1102 2 : sOptions.nYSize = static_cast<int>(ySize);
1103 : }
1104 4 : return sOptions.nXSize > 0 && sOptions.nYSize > 0;
1105 61 : };
1106 :
1107 : const int nLayerCount =
1108 113 : (sOptions.osSQL.empty() && sOptions.aosLayers.empty())
1109 122 : ? 1
1110 38 : : static_cast<int>(sOptions.aosLayers.size());
1111 :
1112 61 : const bool bOneSizeNeedsCalculation{
1113 61 : static_cast<bool>((sOptions.nXSize == 0) ^ (sOptions.nYSize == 0))};
1114 :
1115 : // Calculate the size if either nXSize or nYSize is 0
1116 61 : if (sOptions.osSQL.empty() && bOneSizeNeedsCalculation)
1117 : {
1118 2 : CPLErr eErr = CE_None;
1119 : // Get the extent of the source dataset
1120 2 : OGREnvelope sEnvelope;
1121 2 : bool bFirstLayer = true;
1122 4 : for (int i = 0; i < nLayerCount; i++)
1123 : {
1124 : OGRLayerH hLayer;
1125 2 : if (sOptions.aosLayers.size() > static_cast<size_t>(i))
1126 2 : hLayer = GDALDatasetGetLayerByName(
1127 2 : hSrcDataset, sOptions.aosLayers[i].c_str());
1128 : else
1129 0 : hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
1130 2 : if (hLayer == nullptr)
1131 : {
1132 0 : CPLError(CE_Failure, CPLE_AppDefined,
1133 : "Unable to find layer \"%s\".",
1134 0 : sOptions.aosLayers.size() > static_cast<size_t>(i)
1135 0 : ? sOptions.aosLayers[i].c_str()
1136 : : "0");
1137 0 : eErr = CE_Failure;
1138 0 : break;
1139 : }
1140 2 : OGREnvelope sLayerEnvelop;
1141 2 : if (OGR_L_GetExtent(hLayer, &sLayerEnvelop, TRUE) != OGRERR_NONE)
1142 : {
1143 0 : CPLError(CE_Failure, CPLE_AppDefined,
1144 : "Cannot get layer extent");
1145 0 : eErr = CE_Failure;
1146 0 : break;
1147 : }
1148 2 : if (bFirstLayer)
1149 : {
1150 2 : sEnvelope = sLayerEnvelop;
1151 2 : bFirstLayer = false;
1152 : }
1153 : else
1154 : {
1155 0 : sEnvelope.Merge(sLayerEnvelop);
1156 : }
1157 : }
1158 :
1159 2 : if (!calculateSize(sEnvelope))
1160 : {
1161 0 : CPLError(CE_Failure, CPLE_AppDefined,
1162 : "Cannot calculate size from layer extent");
1163 0 : eErr = CE_Failure;
1164 : }
1165 :
1166 2 : if (eErr == CE_Failure)
1167 : {
1168 0 : return nullptr;
1169 : }
1170 : }
1171 :
1172 28 : const auto GetOutputDataType = [&](OGRLayerH hLayer)
1173 : {
1174 28 : CPLAssert(bCreateOutput);
1175 28 : CPLAssert(hDriver);
1176 57 : GDALDataType eOutputType = sOptions.eOutputType;
1177 28 : if (eOutputType == GDT_Unknown && !sOptions.osBurnAttribute.empty())
1178 : {
1179 1 : OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer);
1180 1 : const int iBurnField = OGR_FD_GetFieldIndex(
1181 : hLayerDefn, sOptions.osBurnAttribute.c_str());
1182 1 : if (iBurnField >= 0 && OGR_Fld_GetType(OGR_FD_GetFieldDefn(
1183 : hLayerDefn, iBurnField)) == OFTInteger64)
1184 : {
1185 1 : const char *pszMD = GDALGetMetadataItem(
1186 : hDriver, GDAL_DMD_CREATIONDATATYPES, nullptr);
1187 2 : if (pszMD && CPLStringList(CSLTokenizeString2(pszMD, " ", 0))
1188 1 : .FindString("Int64") >= 0)
1189 : {
1190 1 : eOutputType = GDT_Int64;
1191 : }
1192 : }
1193 : }
1194 28 : if (eOutputType == GDT_Unknown)
1195 : {
1196 27 : eOutputType = GDT_Float64;
1197 : }
1198 28 : return eOutputType;
1199 61 : };
1200 :
1201 : // Store SRS handle
1202 : OGRSpatialReferenceH hSRS =
1203 61 : sOptions.oOutputSRS.IsEmpty()
1204 61 : ? nullptr
1205 2 : : OGRSpatialReference::ToHandle(
1206 61 : const_cast<OGRSpatialReference *>(&sOptions.oOutputSRS));
1207 :
1208 : /* -------------------------------------------------------------------- */
1209 : /* Process SQL request. */
1210 : /* -------------------------------------------------------------------- */
1211 61 : CPLErr eErr = CE_Failure;
1212 :
1213 61 : if (!sOptions.osSQL.empty())
1214 : {
1215 : OGRLayerH hLayer =
1216 9 : GDALDatasetExecuteSQL(hSrcDataset, sOptions.osSQL.c_str(), nullptr,
1217 9 : sOptions.osDialect.c_str());
1218 9 : if (hLayer != nullptr)
1219 : {
1220 :
1221 9 : if (bOneSizeNeedsCalculation)
1222 : {
1223 3 : OGREnvelope sEnvelope;
1224 : bool bSizeCalculationError{
1225 3 : OGR_L_GetExtent(hLayer, &sEnvelope, TRUE) != OGRERR_NONE};
1226 3 : if (!bSizeCalculationError)
1227 : {
1228 2 : bSizeCalculationError = !calculateSize(sEnvelope);
1229 : }
1230 :
1231 3 : if (bSizeCalculationError)
1232 : {
1233 1 : CPLError(CE_Failure, CPLE_AppDefined,
1234 : "Cannot get layer extent");
1235 1 : GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
1236 1 : return nullptr;
1237 : }
1238 : }
1239 :
1240 8 : if (bCreateOutput)
1241 : {
1242 4 : std::vector<OGRLayerH> ahLayers;
1243 4 : ahLayers.push_back(hLayer);
1244 :
1245 4 : const GDALDataType eOutputType = GetOutputDataType(hLayer);
1246 12 : poNewOutDS = CreateOutputDataset(
1247 : ahLayers, hSRS, sOptions.sEnvelop, hDriver, pszDest,
1248 : sOptions.nXSize, sOptions.nYSize, sOptions.dfXRes,
1249 4 : sOptions.dfYRes, sOptions.bTargetAlignedPixels,
1250 4 : static_cast<int>(sOptions.anBandList.size()), eOutputType,
1251 : sOptions.aosCreationOptions, sOptions.adfInitVals,
1252 8 : sOptions.osNoData.c_str());
1253 4 : if (poNewOutDS == nullptr)
1254 : {
1255 0 : GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
1256 0 : return nullptr;
1257 : }
1258 4 : poOutDS = poNewOutDS.get();
1259 : }
1260 :
1261 : const bool bCloseReportsProgress =
1262 8 : bCreateOutput && poOutDS->GetCloseReportsProgress();
1263 :
1264 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1265 : pScaledProgressArg(GDALCreateScaledProgress(
1266 : 0.0, bCloseReportsProgress ? 0.5 : 1.0,
1267 : sOptions.pfnProgress,
1268 : sOptions.pProgressData),
1269 16 : GDALDestroyScaledProgress);
1270 :
1271 24 : eErr = ProcessLayer(
1272 : hLayer, hSRS != nullptr, poOutDS, sOptions.anBandList,
1273 8 : sOptions.adfBurnValues, sOptions.b3D, sOptions.bInverse,
1274 : sOptions.osBurnAttribute.c_str(), sOptions.aosRasterizeOptions,
1275 8 : sOptions.aosTO, GDALScaledProgress, pScaledProgressArg.get());
1276 :
1277 8 : GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
1278 : }
1279 : }
1280 :
1281 : /* -------------------------------------------------------------------- */
1282 : /* Create output file if necessary. */
1283 : /* -------------------------------------------------------------------- */
1284 :
1285 60 : if (bCreateOutput && poOutDS == nullptr)
1286 : {
1287 27 : std::vector<OGRLayerH> ahLayers;
1288 :
1289 27 : GDALDataType eOutputType = sOptions.eOutputType;
1290 :
1291 53 : for (int i = 0; i < nLayerCount; i++)
1292 : {
1293 : OGRLayerH hLayer;
1294 27 : if (sOptions.aosLayers.size() > static_cast<size_t>(i))
1295 15 : hLayer = GDALDatasetGetLayerByName(
1296 15 : hSrcDataset, sOptions.aosLayers[i].c_str());
1297 : else
1298 12 : hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
1299 27 : if (hLayer == nullptr)
1300 : {
1301 1 : CPLError(CE_Failure, CPLE_AppDefined,
1302 : "Unable to find layer \"%s\".",
1303 1 : sOptions.aosLayers.size() > static_cast<size_t>(i)
1304 1 : ? sOptions.aosLayers[i].c_str()
1305 : : "0");
1306 1 : return nullptr;
1307 : }
1308 26 : if (eOutputType == GDT_Unknown)
1309 : {
1310 24 : if (GetOutputDataType(hLayer) == GDT_Int64)
1311 1 : eOutputType = GDT_Int64;
1312 : }
1313 :
1314 26 : ahLayers.push_back(hLayer);
1315 : }
1316 :
1317 26 : if (eOutputType == GDT_Unknown)
1318 : {
1319 23 : eOutputType = GDT_Float64;
1320 : }
1321 :
1322 78 : poNewOutDS = CreateOutputDataset(
1323 : ahLayers, hSRS, sOptions.sEnvelop, hDriver, pszDest,
1324 : sOptions.nXSize, sOptions.nYSize, sOptions.dfXRes, sOptions.dfYRes,
1325 26 : sOptions.bTargetAlignedPixels,
1326 26 : static_cast<int>(sOptions.anBandList.size()), eOutputType,
1327 : sOptions.aosCreationOptions, sOptions.adfInitVals,
1328 52 : sOptions.osNoData.c_str());
1329 26 : if (poNewOutDS == nullptr)
1330 : {
1331 4 : return nullptr;
1332 : }
1333 22 : poOutDS = poNewOutDS.get();
1334 : }
1335 :
1336 : const bool bCloseReportsProgress =
1337 55 : bCreateOutput && poOutDS->GetCloseReportsProgress();
1338 :
1339 : /* -------------------------------------------------------------------- */
1340 : /* Process each layer. */
1341 : /* -------------------------------------------------------------------- */
1342 :
1343 101 : for (int i = 0; i < nLayerCount; i++)
1344 : {
1345 : OGRLayerH hLayer;
1346 47 : if (sOptions.aosLayers.size() > static_cast<size_t>(i))
1347 28 : hLayer = GDALDatasetGetLayerByName(hSrcDataset,
1348 28 : sOptions.aosLayers[i].c_str());
1349 : else
1350 19 : hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
1351 47 : if (hLayer == nullptr)
1352 : {
1353 0 : CPLError(CE_Failure, CPLE_AppDefined,
1354 : "Unable to find layer \"%s\".",
1355 0 : sOptions.aosLayers.size() > static_cast<size_t>(i)
1356 0 : ? sOptions.aosLayers[i].c_str()
1357 : : "0");
1358 0 : eErr = CE_Failure;
1359 1 : break;
1360 : }
1361 :
1362 47 : if (!sOptions.osWHERE.empty())
1363 : {
1364 1 : if (OGR_L_SetAttributeFilter(hLayer, sOptions.osWHERE.c_str()) !=
1365 : OGRERR_NONE)
1366 : {
1367 0 : eErr = CE_Failure;
1368 0 : break;
1369 : }
1370 : }
1371 :
1372 47 : const double dfFactor = bCloseReportsProgress ? 0.5 : 1.0;
1373 :
1374 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1375 : pScaledProgressArg(
1376 47 : GDALCreateScaledProgress(dfFactor * i / nLayerCount,
1377 47 : dfFactor * (i + 1) / nLayerCount,
1378 : sOptions.pfnProgress,
1379 : sOptions.pProgressData),
1380 47 : GDALDestroyScaledProgress);
1381 :
1382 141 : eErr = ProcessLayer(hLayer, !sOptions.oOutputSRS.IsEmpty(), poOutDS,
1383 : sOptions.anBandList, sOptions.adfBurnValues,
1384 47 : sOptions.b3D, sOptions.bInverse,
1385 : sOptions.osBurnAttribute.c_str(),
1386 : sOptions.aosRasterizeOptions, sOptions.aosTO,
1387 47 : GDALScaledProgress, pScaledProgressArg.get());
1388 47 : if (eErr != CE_None)
1389 1 : break;
1390 : }
1391 :
1392 55 : if (eErr != CE_None)
1393 : {
1394 1 : return nullptr;
1395 : }
1396 :
1397 54 : if (bCloseReportsProgress)
1398 : {
1399 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1400 : pScaledProgressArg(GDALCreateScaledProgress(0.5, 1.0,
1401 : sOptions.pfnProgress,
1402 : sOptions.pProgressData),
1403 1 : GDALDestroyScaledProgress);
1404 :
1405 : const bool bCanReopenWithCurrentDescription =
1406 1 : poOutDS->CanReopenWithCurrentDescription();
1407 :
1408 1 : eErr = poOutDS->Close(GDALScaledProgress, pScaledProgressArg.get());
1409 1 : poOutDS = nullptr;
1410 1 : if (eErr != CE_None)
1411 0 : return nullptr;
1412 :
1413 1 : if (bCanReopenWithCurrentDescription)
1414 : {
1415 : {
1416 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1417 1 : poNewOutDS.reset(
1418 : GDALDataset::Open(pszDest, GDAL_OF_RASTER | GDAL_OF_UPDATE,
1419 : nullptr, nullptr, nullptr));
1420 : }
1421 1 : if (!poNewOutDS)
1422 : {
1423 1 : poNewOutDS.reset(GDALDataset::Open(
1424 : pszDest, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
1425 : nullptr, nullptr));
1426 : }
1427 : }
1428 : else
1429 : {
1430 : struct DummyDataset final : public GDALDataset
1431 : {
1432 0 : DummyDataset() = default;
1433 : };
1434 :
1435 0 : poNewOutDS = std::make_unique<DummyDataset>();
1436 : }
1437 : }
1438 :
1439 54 : return poNewOutDS ? poNewOutDS.release() : poOutDS;
1440 : }
1441 :
1442 : /************************************************************************/
1443 : /* ArgIsNumericRasterize() */
1444 : /************************************************************************/
1445 :
1446 401 : static bool ArgIsNumericRasterize(const char *pszArg)
1447 :
1448 : {
1449 401 : char *pszEnd = nullptr;
1450 401 : CPLStrtod(pszArg, &pszEnd);
1451 401 : return pszEnd != nullptr && pszEnd[0] == '\0';
1452 : }
1453 :
1454 : /************************************************************************/
1455 : /* GDALRasterizeOptionsNew() */
1456 : /************************************************************************/
1457 :
1458 : /**
1459 : * Allocates a GDALRasterizeOptions struct.
1460 : *
1461 : * @param papszArgv NULL terminated list of options (potentially including
1462 : * filename and open options too), or NULL. The accepted options are the ones of
1463 : * the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
1464 : * @param psOptionsForBinary (output) may be NULL (and should generally be
1465 : * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1466 : * GDALRasterizeOptionsForBinaryNew() prior to this
1467 : * function. Will be filled with potentially present filename, open options,...
1468 : * @return pointer to the allocated GDALRasterizeOptions struct. Must be freed
1469 : * with GDALRasterizeOptionsFree().
1470 : *
1471 : * @since GDAL 2.1
1472 : */
1473 :
1474 : GDALRasterizeOptions *
1475 67 : GDALRasterizeOptionsNew(char **papszArgv,
1476 : GDALRasterizeOptionsForBinary *psOptionsForBinary)
1477 : {
1478 :
1479 134 : auto psOptions = std::make_unique<GDALRasterizeOptions>();
1480 :
1481 : /*-------------------------------------------------------------------- */
1482 : /* Parse arguments. */
1483 : /*-------------------------------------------------------------------- */
1484 :
1485 134 : CPLStringList aosArgv;
1486 :
1487 : /* -------------------------------------------------------------------- */
1488 : /* Pre-processing for custom syntax that ArgumentParser does not */
1489 : /* support. */
1490 : /* -------------------------------------------------------------------- */
1491 67 : const int argc = CSLCount(papszArgv);
1492 682 : for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
1493 : i++)
1494 : {
1495 : // argparser will be confused if the value of a string argument
1496 : // starts with a negative sign.
1497 615 : if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
1498 : {
1499 5 : ++i;
1500 5 : psOptions->osNoData = papszArgv[i];
1501 5 : psOptions->bCreateOutput = true;
1502 : }
1503 :
1504 : // argparser is confused by arguments that have at_least_one
1505 : // cardinality, if they immediately precede positional arguments.
1506 610 : else if (EQUAL(papszArgv[i], "-burn") && papszArgv[i + 1])
1507 : {
1508 112 : if (strchr(papszArgv[i + 1], ' '))
1509 : {
1510 : const CPLStringList aosTokens(
1511 0 : CSLTokenizeString(papszArgv[i + 1]));
1512 0 : for (const char *pszToken : aosTokens)
1513 : {
1514 0 : psOptions->adfBurnValues.push_back(CPLAtof(pszToken));
1515 : }
1516 0 : i += 1;
1517 : }
1518 : else
1519 : {
1520 224 : while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
1521 : {
1522 112 : psOptions->adfBurnValues.push_back(
1523 112 : CPLAtof(papszArgv[i + 1]));
1524 112 : i += 1;
1525 : }
1526 : }
1527 :
1528 : // Dummy value to make argparse happy, as at least one of
1529 : // -burn, -a or -3d is required
1530 112 : aosArgv.AddString("-burn");
1531 112 : aosArgv.AddString("0");
1532 : }
1533 498 : else if (EQUAL(papszArgv[i], "-init") && papszArgv[i + 1])
1534 : {
1535 27 : if (strchr(papszArgv[i + 1], ' '))
1536 : {
1537 : const CPLStringList aosTokens(
1538 0 : CSLTokenizeString(papszArgv[i + 1]));
1539 0 : for (const char *pszToken : aosTokens)
1540 : {
1541 0 : psOptions->adfInitVals.push_back(CPLAtof(pszToken));
1542 : }
1543 0 : i += 1;
1544 : }
1545 : else
1546 : {
1547 54 : while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
1548 : {
1549 27 : psOptions->adfInitVals.push_back(CPLAtof(papszArgv[i + 1]));
1550 27 : i += 1;
1551 : }
1552 : }
1553 27 : psOptions->bCreateOutput = true;
1554 : }
1555 471 : else if (EQUAL(papszArgv[i], "-b") && papszArgv[i + 1])
1556 : {
1557 66 : if (strchr(papszArgv[i + 1], ' '))
1558 : {
1559 : const CPLStringList aosTokens(
1560 0 : CSLTokenizeString(papszArgv[i + 1]));
1561 0 : for (const char *pszToken : aosTokens)
1562 : {
1563 0 : psOptions->anBandList.push_back(atoi(pszToken));
1564 : }
1565 0 : i += 1;
1566 : }
1567 : else
1568 : {
1569 132 : while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
1570 : {
1571 66 : psOptions->anBandList.push_back(atoi(papszArgv[i + 1]));
1572 66 : i += 1;
1573 : }
1574 66 : }
1575 : }
1576 : else
1577 : {
1578 405 : aosArgv.AddString(papszArgv[i]);
1579 : }
1580 : }
1581 :
1582 : try
1583 : {
1584 : auto argParser =
1585 69 : GDALRasterizeOptionsGetParser(psOptions.get(), psOptionsForBinary);
1586 67 : argParser->parse_args_without_binary_name(aosArgv.List());
1587 :
1588 : // Check all no store_into args
1589 68 : if (auto oTe = argParser->present<std::vector<double>>("-te"))
1590 : {
1591 3 : psOptions->sEnvelop.MinX = oTe.value()[0];
1592 3 : psOptions->sEnvelop.MinY = oTe.value()[1];
1593 3 : psOptions->sEnvelop.MaxX = oTe.value()[2];
1594 3 : psOptions->sEnvelop.MaxY = oTe.value()[3];
1595 3 : psOptions->bCreateOutput = true;
1596 : }
1597 :
1598 65 : if (auto oTr = argParser->present<std::vector<double>>("-tr"))
1599 : {
1600 13 : psOptions->dfXRes = oTr.value()[0];
1601 13 : psOptions->dfYRes = oTr.value()[1];
1602 :
1603 13 : if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
1604 : {
1605 0 : CPLError(CE_Failure, CPLE_AppDefined,
1606 : "Wrong value for -tr parameter.");
1607 0 : return nullptr;
1608 : }
1609 :
1610 13 : psOptions->bCreateOutput = true;
1611 : }
1612 :
1613 65 : if (auto oTs = argParser->present<std::vector<double>>("-ts"))
1614 : {
1615 22 : const int nXSize = static_cast<int>(oTs.value()[0]);
1616 22 : const int nYSize = static_cast<int>(oTs.value()[1]);
1617 :
1618 : // Warn the user if the conversion to int looses precision
1619 22 : if (nXSize != oTs.value()[0] || nYSize != oTs.value()[1])
1620 : {
1621 1 : CPLError(CE_Warning, CPLE_AppDefined,
1622 : "-ts values parsed as %d %d.", nXSize, nYSize);
1623 : }
1624 :
1625 22 : psOptions->nXSize = nXSize;
1626 22 : psOptions->nYSize = nYSize;
1627 :
1628 22 : if (!(psOptions->nXSize > 0 || psOptions->nYSize > 0))
1629 : {
1630 0 : CPLError(CE_Failure, CPLE_AppDefined,
1631 : "Wrong value for -ts parameter: at least one of the "
1632 : "arguments must be greater than zero.");
1633 0 : return nullptr;
1634 : }
1635 :
1636 22 : psOptions->bCreateOutput = true;
1637 : }
1638 :
1639 65 : if (psOptions->bCreateOutput)
1640 : {
1641 57 : if (psOptions->dfXRes == 0 && psOptions->dfYRes == 0 &&
1642 57 : psOptions->nXSize == 0 && psOptions->nYSize == 0)
1643 : {
1644 0 : CPLError(CE_Failure, CPLE_NotSupported,
1645 : "'-tr xres yres' or '-ts xsize ysize' is required.");
1646 0 : return nullptr;
1647 : }
1648 :
1649 35 : if (psOptions->bTargetAlignedPixels && psOptions->dfXRes == 0 &&
1650 0 : psOptions->dfYRes == 0)
1651 : {
1652 0 : CPLError(CE_Failure, CPLE_NotSupported,
1653 : "-tap option cannot be used without using -tr.");
1654 0 : return nullptr;
1655 : }
1656 :
1657 35 : if (!psOptions->anBandList.empty())
1658 : {
1659 1 : CPLError(
1660 : CE_Failure, CPLE_NotSupported,
1661 : "-b option cannot be used when creating a GDAL dataset.");
1662 1 : return nullptr;
1663 : }
1664 :
1665 34 : int nBandCount = 1;
1666 :
1667 34 : if (!psOptions->adfBurnValues.empty())
1668 19 : nBandCount = static_cast<int>(psOptions->adfBurnValues.size());
1669 :
1670 34 : if (static_cast<int>(psOptions->adfInitVals.size()) > nBandCount)
1671 0 : nBandCount = static_cast<int>(psOptions->adfInitVals.size());
1672 :
1673 34 : if (psOptions->adfInitVals.size() == 1)
1674 : {
1675 3 : for (int i = 1; i <= nBandCount - 1; i++)
1676 0 : psOptions->adfInitVals.push_back(psOptions->adfInitVals[0]);
1677 : }
1678 :
1679 96 : for (int i = 1; i <= nBandCount; i++)
1680 62 : psOptions->anBandList.push_back(i);
1681 : }
1682 : else
1683 : {
1684 30 : if (psOptions->anBandList.empty())
1685 11 : psOptions->anBandList.push_back(1);
1686 : }
1687 :
1688 64 : if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
1689 0 : !psOptions->osSQL.empty())
1690 : {
1691 0 : CPLError(CE_Warning, CPLE_AppDefined,
1692 : "-dialect is ignored with -where. Use -sql instead");
1693 : }
1694 :
1695 64 : if (psOptionsForBinary)
1696 : {
1697 11 : psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
1698 11 : if (!psOptions->osFormat.empty())
1699 0 : psOptionsForBinary->osFormat = psOptions->osFormat;
1700 : }
1701 70 : else if (psOptions->adfBurnValues.empty() &&
1702 70 : psOptions->osBurnAttribute.empty() && !psOptions->b3D)
1703 : {
1704 12 : psOptions->adfBurnValues.push_back(255);
1705 : }
1706 : }
1707 2 : catch (const std::exception &e)
1708 : {
1709 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1710 2 : return nullptr;
1711 : }
1712 :
1713 64 : return psOptions.release();
1714 : }
1715 :
1716 : /************************************************************************/
1717 : /* GDALRasterizeOptionsFree() */
1718 : /************************************************************************/
1719 :
1720 : /**
1721 : * Frees the GDALRasterizeOptions struct.
1722 : *
1723 : * @param psOptions the options struct for GDALRasterize().
1724 : *
1725 : * @since GDAL 2.1
1726 : */
1727 :
1728 64 : void GDALRasterizeOptionsFree(GDALRasterizeOptions *psOptions)
1729 : {
1730 64 : delete psOptions;
1731 64 : }
1732 :
1733 : /************************************************************************/
1734 : /* GDALRasterizeOptionsSetProgress() */
1735 : /************************************************************************/
1736 :
1737 : /**
1738 : * Set a progress function.
1739 : *
1740 : * @param psOptions the options struct for GDALRasterize().
1741 : * @param pfnProgress the progress callback.
1742 : * @param pProgressData the user data for the progress callback.
1743 : *
1744 : * @since GDAL 2.1
1745 : */
1746 :
1747 40 : void GDALRasterizeOptionsSetProgress(GDALRasterizeOptions *psOptions,
1748 : GDALProgressFunc pfnProgress,
1749 : void *pProgressData)
1750 : {
1751 40 : psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1752 40 : psOptions->pProgressData = pProgressData;
1753 40 : }
|