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