Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "vector rasterize" subcommand
5 : * Author: Alessandro Pasotti <elpaso at itopen dot it>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Alessandro Pasotti <elpaso at itopen dot it>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include <cmath>
14 :
15 : #include "gdalalg_vector_rasterize.h"
16 : #include "gdalalg_raster_write.h"
17 :
18 : #include "cpl_conv.h"
19 : #include "gdal_priv.h"
20 : #include "gdal_utils.h"
21 :
22 : //! @cond Doxygen_Suppress
23 :
24 : #ifndef _
25 : #define _(x) (x)
26 : #endif
27 :
28 : /************************************************************************/
29 : /* GDALVectorRasterizeAlgorithm::GDALVectorRasterizeAlgorithm() */
30 : /************************************************************************/
31 :
32 123 : GDALVectorRasterizeAlgorithm::GDALVectorRasterizeAlgorithm(bool bStandaloneStep)
33 : : GDALPipelineStepAlgorithm(
34 : NAME, DESCRIPTION, HELP_URL,
35 0 : ConstructorOptions()
36 123 : .SetStandaloneStep(bStandaloneStep)
37 246 : .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
38 : {
39 123 : if (bStandaloneStep)
40 : {
41 104 : AddProgressArg();
42 104 : AddOutputFormatArg(&m_format)
43 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
44 416 : {GDAL_DCAP_RASTER, GDAL_DCAP_CREATE})
45 208 : .AddMetadataItem(GAAMDI_VRT_COMPATIBLE, {"false"});
46 104 : AddOpenOptionsArg(&m_openOptions);
47 104 : AddInputFormatsArg(&m_inputFormats)
48 208 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
49 104 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR)
50 104 : .SetMinCount(1)
51 104 : .SetMaxCount(1);
52 104 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER)
53 104 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
54 104 : AddCreationOptionsArg(&m_creationOptions);
55 104 : AddOverwriteArg(&m_overwrite);
56 : }
57 : else
58 : {
59 19 : AddVectorHiddenInputDatasetArg();
60 : }
61 :
62 123 : AddBandArg(&m_bands, _("The band(s) to burn values into (1-based index)"));
63 246 : AddArg("invert", 0, _("Invert the rasterization"), &m_invert)
64 123 : .SetDefault(false);
65 : AddArg("all-touched", 0, _("Enables the ALL_TOUCHED rasterization option"),
66 123 : &m_allTouched);
67 123 : AddArg("burn", 0, _("Burn value"), &m_burnValues);
68 123 : AddArg("attribute-name", 'a', _("Attribute name"), &m_attributeName);
69 : AddArg("3d", 0,
70 : _("Indicates that a burn value should be extracted from the Z"
71 : " values of the feature"),
72 123 : &m_3d);
73 123 : AddLayerNameArg(&m_layerName).SetMutualExclusionGroup("layer-name-or-sql");
74 123 : AddArg("where", 0, _("SQL where clause"), &m_where);
75 246 : AddArg("sql", 0, _("SQL select statement"), &m_sql)
76 123 : .SetMutualExclusionGroup("layer-name-or-sql");
77 123 : AddArg("dialect", 0, _("SQL dialect"), &m_dialect);
78 : AddArg("nodata", 0, _("Assign a specified nodata value to output bands"),
79 123 : &m_nodata);
80 : AddArg("init", 0, _("Pre-initialize output bands with specified value"),
81 123 : &m_initValues);
82 : AddArg("like", 0,
83 : _("Dataset to use as a template for bounds, CRS and resolution"),
84 246 : &m_likeDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR)
85 123 : .SetMetaVar("DATASET");
86 246 : AddArg("crs", 0, _("Override the projection for the output file"), &m_srs)
87 246 : .AddHiddenAlias("srs")
88 123 : .SetIsCRSArg(/*noneAllowed=*/false);
89 : AddArg("transformer-option", 0,
90 : _("Set a transformer option suitable to pass to "
91 : "GDALCreateGenImgProjTransformer2"),
92 246 : &m_transformerOption)
93 123 : .SetMetaVar("<NAME>=<VALUE>");
94 : AddArg("extent", 0, _("Set the target georeferenced extent"),
95 246 : &m_targetExtent)
96 123 : .SetMinCount(4)
97 123 : .SetMaxCount(4)
98 123 : .SetRepeatedArgAllowed(false)
99 123 : .SetMetaVar("<xmin>,<ymin>,<xmax>,<ymax>");
100 : auto &argResolution =
101 : AddArg("resolution", 0, _("Set the target resolution"),
102 246 : &m_targetResolution)
103 123 : .SetMinCount(2)
104 123 : .SetMaxCount(2)
105 123 : .SetRepeatedArgAllowed(false)
106 246 : .SetMetaVar("<xres>,<yres>")
107 123 : .SetMutualExclusionGroup("size-or-resolution");
108 : AddArg("target-aligned-pixels", 0,
109 : _("(target aligned pixels) Align the coordinates of the extent of "
110 : "the output file to the values of the resolution"),
111 246 : &m_tap)
112 246 : .AddAlias("tap")
113 123 : .AddDirectDependency(argResolution);
114 : AddArg("size", 0, _("Set the target size in pixels and lines"),
115 246 : &m_targetSize)
116 123 : .SetMinCount(2)
117 123 : .SetMaxCount(2)
118 123 : .SetRepeatedArgAllowed(false)
119 246 : .SetMetaVar("<xsize>,<ysize>")
120 123 : .SetMutualExclusionGroup("size-or-resolution");
121 123 : AddOutputDataTypeArg(&m_outputType);
122 : AddArg("optimization", 0,
123 : _("Force the algorithm used (results are identical)"),
124 246 : &m_optimization)
125 123 : .SetChoices("AUTO", "RASTER", "VECTOR")
126 123 : .SetDefault("AUTO");
127 :
128 123 : if (bStandaloneStep)
129 : {
130 208 : auto &addArg = AddArg("add", 0, _("Add to existing raster"), &m_add)
131 104 : .SetDefault(false);
132 104 : auto &updateArg = AddUpdateArg(&m_update);
133 : addArg.AddValidationAction(
134 3 : [&updateArg]()
135 : {
136 3 : updateArg.Set(true);
137 3 : return true;
138 104 : });
139 : }
140 :
141 123 : AddValidationAction(
142 99 : [this]()
143 : {
144 97 : if (m_likeDataset.GetDatasetRef() &&
145 2 : (!m_targetExtent.empty() || !m_srs.empty()))
146 : {
147 1 : ReportError(
148 : CE_Failure, CPLE_AppDefined,
149 : "--like is mutually exclusive with --extent and --crs");
150 1 : return false;
151 : }
152 94 : return true;
153 : });
154 123 : }
155 :
156 : /************************************************************************/
157 : /* GDALVectorRasterizeAlgorithm::RunStep() */
158 : /************************************************************************/
159 :
160 55 : bool GDALVectorRasterizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
161 : {
162 55 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
163 55 : CPLAssert(poSrcDS);
164 :
165 110 : CPLStringList aosOptions;
166 :
167 55 : if (m_bands.size())
168 : {
169 72 : for (int band : m_bands)
170 : {
171 54 : aosOptions.AddString("-b");
172 54 : aosOptions.AddString(CPLSPrintf("%d", band));
173 : }
174 : }
175 :
176 55 : if (m_invert)
177 : {
178 1 : aosOptions.AddString("-i");
179 : }
180 :
181 55 : if (m_allTouched)
182 : {
183 31 : aosOptions.AddString("-at");
184 : }
185 :
186 55 : if (m_burnValues.size())
187 : {
188 150 : for (double burnValue : m_burnValues)
189 : {
190 104 : aosOptions.AddString("-burn");
191 104 : aosOptions.AddString(CPLSPrintf("%.17g", burnValue));
192 : }
193 : }
194 :
195 55 : if (!m_attributeName.empty())
196 : {
197 5 : aosOptions.AddString("-a");
198 5 : aosOptions.AddString(m_attributeName.c_str());
199 : }
200 :
201 55 : if (m_3d)
202 : {
203 2 : aosOptions.AddString("-3d");
204 : }
205 :
206 55 : if (m_add)
207 : {
208 1 : aosOptions.AddString("-add");
209 : // Implies update
210 1 : m_update = true;
211 : }
212 :
213 55 : if (!m_layerName.empty())
214 : {
215 22 : aosOptions.AddString("-l");
216 22 : aosOptions.AddString(m_layerName.c_str());
217 : }
218 :
219 55 : if (!m_where.empty())
220 : {
221 1 : aosOptions.AddString("-where");
222 1 : aosOptions.AddString(m_where.c_str());
223 : }
224 :
225 55 : if (!m_sql.empty())
226 : {
227 7 : aosOptions.AddString("-sql");
228 7 : aosOptions.AddString(m_sql.c_str());
229 : }
230 :
231 55 : if (!m_dialect.empty())
232 : {
233 3 : aosOptions.AddString("-dialect");
234 3 : aosOptions.AddString(m_dialect.c_str());
235 : }
236 :
237 110 : std::string outputFilename;
238 55 : if (m_standaloneStep)
239 : {
240 53 : outputFilename = m_outputDataset.GetName();
241 53 : if (!m_format.empty())
242 : {
243 5 : aosOptions.AddString("-of");
244 5 : aosOptions.AddString(m_format.c_str());
245 : }
246 :
247 54 : for (const std::string &co : m_creationOptions)
248 : {
249 1 : aosOptions.AddString("-co");
250 1 : aosOptions.AddString(co.c_str());
251 : }
252 : }
253 : else
254 : {
255 2 : outputFilename = CPLGenerateTempFilenameSafe("_rasterize.tif");
256 :
257 2 : aosOptions.AddString("-of");
258 2 : aosOptions.AddString("GTiff");
259 :
260 2 : aosOptions.AddString("-co");
261 2 : aosOptions.AddString("TILED=YES");
262 : }
263 :
264 55 : if (!std::isnan(m_nodata))
265 : {
266 2 : if (m_update)
267 : {
268 1 : ReportError(
269 : CE_Failure, CPLE_AppDefined,
270 : "Cannot specify --nodata when updating an existing raster.");
271 1 : return false;
272 : }
273 1 : aosOptions.AddString("-a_nodata");
274 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_nodata));
275 : }
276 :
277 54 : if (m_initValues.size())
278 : {
279 36 : for (double initValue : m_initValues)
280 : {
281 27 : aosOptions.AddString("-init");
282 27 : aosOptions.AddString(CPLSPrintf("%.17g", initValue));
283 : }
284 : }
285 :
286 54 : if (auto *poLikeDS = m_likeDataset.GetDatasetRef())
287 : {
288 1 : OGREnvelope sExtent;
289 1 : if (poLikeDS->GetExtent(&sExtent) != CE_None)
290 : {
291 0 : ReportError(CE_Failure, CPLE_AppDefined,
292 : "Cannot get extent of '%s'",
293 0 : poLikeDS->GetDescription());
294 0 : return false;
295 : }
296 1 : aosOptions.AddString("-te");
297 1 : aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MinX));
298 1 : aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MinY));
299 1 : aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MaxX));
300 1 : aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MaxY));
301 :
302 1 : const auto poSRS = poLikeDS->GetSpatialRef();
303 1 : if (poSRS)
304 : {
305 1 : const char *const options[] = {"FORMAT=WKT2_2019", nullptr};
306 2 : const std::string osCRS = poSRS->exportToWkt(options);
307 1 : aosOptions.AddString("-a_srs");
308 1 : aosOptions.AddString(osCRS.c_str());
309 : }
310 :
311 1 : if (m_targetResolution.empty() && m_targetSize.empty())
312 : {
313 1 : GDALGeoTransform gt;
314 1 : if (poLikeDS->GetGeoTransform(gt) == CE_None)
315 : {
316 1 : if (gt.xrot != 0 || gt.yrot != 0)
317 : {
318 0 : ReportError(CE_Failure, CPLE_NotSupported,
319 : "Geotransform matrix of '%s' has non-zero "
320 : "rotation terms",
321 0 : poLikeDS->GetDescription());
322 0 : return false;
323 : }
324 1 : aosOptions.AddString("-ts");
325 : aosOptions.AddString(
326 1 : CPLSPrintf("%d", poLikeDS->GetRasterXSize()));
327 : aosOptions.AddString(
328 1 : CPLSPrintf("%d", poLikeDS->GetRasterYSize()));
329 : }
330 : }
331 : }
332 : else
333 : {
334 53 : if (m_targetExtent.size())
335 : {
336 1 : aosOptions.AddString("-te");
337 5 : for (double targetExtent : m_targetExtent)
338 : {
339 4 : aosOptions.AddString(CPLSPrintf("%.17g", targetExtent));
340 : }
341 : }
342 :
343 53 : if (!m_srs.empty())
344 : {
345 2 : if (m_update)
346 : {
347 1 : ReportError(
348 : CE_Failure, CPLE_AppDefined,
349 : "Cannot specify --crs when updating an existing raster.");
350 1 : return false;
351 : }
352 1 : aosOptions.AddString("-a_srs");
353 1 : aosOptions.AddString(m_srs.c_str());
354 : }
355 : }
356 :
357 53 : if (m_transformerOption.size())
358 : {
359 0 : for (const auto &to : m_transformerOption)
360 : {
361 0 : aosOptions.AddString("-to");
362 0 : aosOptions.AddString(to.c_str());
363 : }
364 : }
365 :
366 53 : if (m_tap)
367 : {
368 1 : aosOptions.AddString("-tap");
369 : }
370 :
371 53 : if (m_targetResolution.size())
372 : {
373 7 : if (m_update)
374 : {
375 1 : ReportError(CE_Failure, CPLE_AppDefined,
376 : "Cannot specify --resolution when updating an existing "
377 : "raster.");
378 1 : return false;
379 : }
380 6 : aosOptions.AddString("-tr");
381 18 : for (double targetResolution : m_targetResolution)
382 : {
383 12 : aosOptions.AddString(CPLSPrintf("%.17g", targetResolution));
384 : }
385 : }
386 46 : else if (m_targetSize.size())
387 : {
388 26 : if (m_update)
389 : {
390 1 : ReportError(
391 : CE_Failure, CPLE_AppDefined,
392 : "Cannot specify --size when updating an existing raster.");
393 1 : return false;
394 : }
395 25 : aosOptions.AddString("-ts");
396 75 : for (int targetSize : m_targetSize)
397 : {
398 50 : aosOptions.AddString(CPLSPrintf("%d", targetSize));
399 : }
400 : }
401 20 : else if (!m_likeDataset.GetDatasetRef() && !m_outputDataset.GetDatasetRef())
402 : {
403 3 : ReportError(
404 : CE_Failure, CPLE_AppDefined,
405 : "Must specify output resolution (--resolution) or size (--size) "
406 : "when writing rasterized features to a new dataset.");
407 3 : return false;
408 : }
409 :
410 48 : if (!m_outputType.empty())
411 : {
412 2 : if (m_update)
413 : {
414 1 : ReportError(CE_Failure, CPLE_AppDefined,
415 : "Cannot specify --output-data-type when updating an "
416 : "existing raster.");
417 1 : return false;
418 : }
419 1 : aosOptions.AddString("-ot");
420 1 : aosOptions.AddString(m_outputType.c_str());
421 : }
422 :
423 47 : if (!m_optimization.empty())
424 : {
425 47 : aosOptions.AddString("-optim");
426 47 : aosOptions.AddString(m_optimization.c_str());
427 : }
428 :
429 47 : bool bOK = false;
430 : std::unique_ptr<GDALRasterizeOptions, decltype(&GDALRasterizeOptionsFree)>
431 : psOptions{GDALRasterizeOptionsNew(aosOptions.List(), nullptr),
432 47 : GDALRasterizeOptionsFree};
433 47 : if (psOptions)
434 : {
435 44 : GDALRasterizeOptionsSetProgress(psOptions.get(), ctxt.m_pfnProgress,
436 : ctxt.m_pProgressData);
437 :
438 : GDALDatasetH hDstDS =
439 44 : GDALDataset::ToHandle(m_outputDataset.GetDatasetRef());
440 :
441 44 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
442 :
443 44 : auto poRetDS = GDALDataset::FromHandle(GDALRasterize(
444 44 : outputFilename.c_str(), hDstDS, hSrcDS, psOptions.get(), nullptr));
445 44 : bOK = poRetDS != nullptr;
446 :
447 44 : if (!hDstDS)
448 : {
449 31 : if (!m_standaloneStep && poRetDS)
450 : {
451 2 : VSIUnlink(outputFilename.c_str());
452 2 : poRetDS->MarkSuppressOnClose();
453 : }
454 :
455 31 : m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
456 : }
457 : }
458 :
459 47 : return bOK;
460 : }
461 :
462 : /************************************************************************/
463 : /* GDALVectorRasterizeAlgorithm::RunImpl() */
464 : /************************************************************************/
465 :
466 53 : bool GDALVectorRasterizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
467 : void *pProgressData)
468 : {
469 53 : GDALPipelineStepRunContext stepCtxt;
470 53 : stepCtxt.m_pfnProgress = pfnProgress;
471 53 : stepCtxt.m_pProgressData = pProgressData;
472 53 : return RunPreStepPipelineValidations() && RunStep(stepCtxt);
473 : }
474 :
475 : GDALVectorRasterizeAlgorithmStandalone::
476 : ~GDALVectorRasterizeAlgorithmStandalone() = default;
477 :
478 : //! @endcond
|