/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 | AddProgressArg(); |
40 | 0 | if (bStandaloneStep) |
41 | 0 | { |
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 | AddArg("resolution", 0, _("Set the target resolution"), &m_targetResolution) |
97 | 0 | .SetMinCount(2) |
98 | 0 | .SetMaxCount(2) |
99 | 0 | .SetRepeatedArgAllowed(false) |
100 | 0 | .SetMetaVar("<xres>,<yres>") |
101 | 0 | .SetMutualExclusionGroup("size-or-resolution"); |
102 | 0 | AddArg("target-aligned-pixels", 0, |
103 | 0 | _("(target aligned pixels) Align the coordinates of the extent of " |
104 | 0 | "the output file to the values of the resolution"), |
105 | 0 | &m_tap) |
106 | 0 | .AddAlias("tap"); |
107 | 0 | AddArg("size", 0, _("Set the target size in pixels and lines"), |
108 | 0 | &m_targetSize) |
109 | 0 | .SetMinCount(2) |
110 | 0 | .SetMaxCount(2) |
111 | 0 | .SetRepeatedArgAllowed(false) |
112 | 0 | .SetMetaVar("<xsize>,<ysize>") |
113 | 0 | .SetMutualExclusionGroup("size-or-resolution"); |
114 | 0 | AddOutputDataTypeArg(&m_outputType); |
115 | 0 | AddArg("optimization", 0, |
116 | 0 | _("Force the algorithm used (results are identical)"), |
117 | 0 | &m_optimization) |
118 | 0 | .SetChoices("AUTO", "RASTER", "VECTOR") |
119 | 0 | .SetDefault("AUTO"); |
120 | |
|
121 | 0 | if (bStandaloneStep) |
122 | 0 | { |
123 | 0 | auto &addArg = AddArg("add", 0, _("Add to existing raster"), &m_add) |
124 | 0 | .SetDefault(false); |
125 | 0 | auto &updateArg = AddUpdateArg(&m_update); |
126 | 0 | addArg.AddValidationAction( |
127 | 0 | [&updateArg]() |
128 | 0 | { |
129 | 0 | updateArg.Set(true); |
130 | 0 | return true; |
131 | 0 | }); |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | /************************************************************************/ |
136 | | /* GDALVectorRasterizeAlgorithm::RunStep() */ |
137 | | /************************************************************************/ |
138 | | |
139 | | bool GDALVectorRasterizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) |
140 | 0 | { |
141 | 0 | auto poSrcDS = m_inputDataset[0].GetDatasetRef(); |
142 | 0 | CPLAssert(poSrcDS); |
143 | | |
144 | 0 | CPLStringList aosOptions; |
145 | |
|
146 | 0 | if (m_bands.size()) |
147 | 0 | { |
148 | 0 | for (int band : m_bands) |
149 | 0 | { |
150 | 0 | aosOptions.AddString("-b"); |
151 | 0 | aosOptions.AddString(CPLSPrintf("%d", band)); |
152 | 0 | } |
153 | 0 | } |
154 | |
|
155 | 0 | if (m_invert) |
156 | 0 | { |
157 | 0 | aosOptions.AddString("-i"); |
158 | 0 | } |
159 | |
|
160 | 0 | if (m_allTouched) |
161 | 0 | { |
162 | 0 | aosOptions.AddString("-at"); |
163 | 0 | } |
164 | |
|
165 | 0 | if (m_burnValues.size()) |
166 | 0 | { |
167 | 0 | for (double burnValue : m_burnValues) |
168 | 0 | { |
169 | 0 | aosOptions.AddString("-burn"); |
170 | 0 | aosOptions.AddString(CPLSPrintf("%.17g", burnValue)); |
171 | 0 | } |
172 | 0 | } |
173 | |
|
174 | 0 | if (!m_attributeName.empty()) |
175 | 0 | { |
176 | 0 | aosOptions.AddString("-a"); |
177 | 0 | aosOptions.AddString(m_attributeName.c_str()); |
178 | 0 | } |
179 | |
|
180 | 0 | if (m_3d) |
181 | 0 | { |
182 | 0 | aosOptions.AddString("-3d"); |
183 | 0 | } |
184 | |
|
185 | 0 | if (m_add) |
186 | 0 | { |
187 | 0 | aosOptions.AddString("-add"); |
188 | | // Implies update |
189 | 0 | m_update = true; |
190 | 0 | } |
191 | |
|
192 | 0 | if (!m_layerName.empty()) |
193 | 0 | { |
194 | 0 | aosOptions.AddString("-l"); |
195 | 0 | aosOptions.AddString(m_layerName.c_str()); |
196 | 0 | } |
197 | |
|
198 | 0 | if (!m_where.empty()) |
199 | 0 | { |
200 | 0 | aosOptions.AddString("-where"); |
201 | 0 | aosOptions.AddString(m_where.c_str()); |
202 | 0 | } |
203 | |
|
204 | 0 | if (!m_sql.empty()) |
205 | 0 | { |
206 | 0 | aosOptions.AddString("-sql"); |
207 | 0 | aosOptions.AddString(m_sql.c_str()); |
208 | 0 | } |
209 | |
|
210 | 0 | if (!m_dialect.empty()) |
211 | 0 | { |
212 | 0 | aosOptions.AddString("-dialect"); |
213 | 0 | aosOptions.AddString(m_dialect.c_str()); |
214 | 0 | } |
215 | |
|
216 | 0 | std::string outputFilename; |
217 | 0 | if (m_standaloneStep) |
218 | 0 | { |
219 | 0 | outputFilename = m_outputDataset.GetName(); |
220 | 0 | if (!m_format.empty()) |
221 | 0 | { |
222 | 0 | aosOptions.AddString("-of"); |
223 | 0 | aosOptions.AddString(m_format.c_str()); |
224 | 0 | } |
225 | |
|
226 | 0 | for (const std::string &co : m_creationOptions) |
227 | 0 | { |
228 | 0 | aosOptions.AddString("-co"); |
229 | 0 | aosOptions.AddString(co.c_str()); |
230 | 0 | } |
231 | 0 | } |
232 | 0 | else |
233 | 0 | { |
234 | 0 | outputFilename = CPLGenerateTempFilenameSafe("_rasterize.tif"); |
235 | |
|
236 | 0 | aosOptions.AddString("-of"); |
237 | 0 | aosOptions.AddString("GTiff"); |
238 | |
|
239 | 0 | aosOptions.AddString("-co"); |
240 | 0 | aosOptions.AddString("TILED=YES"); |
241 | 0 | } |
242 | |
|
243 | 0 | if (!std::isnan(m_nodata)) |
244 | 0 | { |
245 | 0 | if (m_update) |
246 | 0 | { |
247 | 0 | ReportError( |
248 | 0 | CE_Failure, CPLE_AppDefined, |
249 | 0 | "Cannot specify --nodata when updating an existing raster."); |
250 | 0 | return false; |
251 | 0 | } |
252 | 0 | aosOptions.AddString("-a_nodata"); |
253 | 0 | aosOptions.AddString(CPLSPrintf("%.17g", m_nodata)); |
254 | 0 | } |
255 | | |
256 | 0 | if (m_initValues.size()) |
257 | 0 | { |
258 | 0 | for (double initValue : m_initValues) |
259 | 0 | { |
260 | 0 | aosOptions.AddString("-init"); |
261 | 0 | aosOptions.AddString(CPLSPrintf("%.17g", initValue)); |
262 | 0 | } |
263 | 0 | } |
264 | |
|
265 | 0 | if (!m_srs.empty()) |
266 | 0 | { |
267 | 0 | if (m_update) |
268 | 0 | { |
269 | 0 | ReportError( |
270 | 0 | CE_Failure, CPLE_AppDefined, |
271 | 0 | "Cannot specify --crs when updating an existing raster."); |
272 | 0 | return false; |
273 | 0 | } |
274 | 0 | aosOptions.AddString("-a_srs"); |
275 | 0 | aosOptions.AddString(m_srs.c_str()); |
276 | 0 | } |
277 | | |
278 | 0 | if (m_transformerOption.size()) |
279 | 0 | { |
280 | 0 | for (const auto &to : m_transformerOption) |
281 | 0 | { |
282 | 0 | aosOptions.AddString("-to"); |
283 | 0 | aosOptions.AddString(to.c_str()); |
284 | 0 | } |
285 | 0 | } |
286 | |
|
287 | 0 | if (m_targetExtent.size()) |
288 | 0 | { |
289 | 0 | aosOptions.AddString("-te"); |
290 | 0 | for (double targetExtent : m_targetExtent) |
291 | 0 | { |
292 | 0 | aosOptions.AddString(CPLSPrintf("%.17g", targetExtent)); |
293 | 0 | } |
294 | 0 | } |
295 | |
|
296 | 0 | if (m_tap) |
297 | 0 | { |
298 | 0 | aosOptions.AddString("-tap"); |
299 | 0 | } |
300 | |
|
301 | 0 | if (m_targetResolution.size()) |
302 | 0 | { |
303 | 0 | if (m_update) |
304 | 0 | { |
305 | 0 | ReportError(CE_Failure, CPLE_AppDefined, |
306 | 0 | "Cannot specify --resolution when updating an existing " |
307 | 0 | "raster."); |
308 | 0 | return false; |
309 | 0 | } |
310 | 0 | aosOptions.AddString("-tr"); |
311 | 0 | for (double targetResolution : m_targetResolution) |
312 | 0 | { |
313 | 0 | aosOptions.AddString(CPLSPrintf("%.17g", targetResolution)); |
314 | 0 | } |
315 | 0 | } |
316 | 0 | else if (m_targetSize.size()) |
317 | 0 | { |
318 | 0 | if (m_update) |
319 | 0 | { |
320 | 0 | ReportError( |
321 | 0 | CE_Failure, CPLE_AppDefined, |
322 | 0 | "Cannot specify --size when updating an existing raster."); |
323 | 0 | return false; |
324 | 0 | } |
325 | 0 | aosOptions.AddString("-ts"); |
326 | 0 | for (int targetSize : m_targetSize) |
327 | 0 | { |
328 | 0 | aosOptions.AddString(CPLSPrintf("%d", targetSize)); |
329 | 0 | } |
330 | 0 | } |
331 | 0 | else if (m_outputDataset.GetDatasetRef() == nullptr) |
332 | 0 | { |
333 | 0 | ReportError( |
334 | 0 | CE_Failure, CPLE_AppDefined, |
335 | 0 | "Must specify output resolution (--resolution) or size (--size) " |
336 | 0 | "when writing rasterized features to a new dataset."); |
337 | 0 | return false; |
338 | 0 | } |
339 | | |
340 | 0 | if (!m_outputType.empty()) |
341 | 0 | { |
342 | 0 | if (m_update) |
343 | 0 | { |
344 | 0 | ReportError(CE_Failure, CPLE_AppDefined, |
345 | 0 | "Cannot specify --output-data-type when updating an " |
346 | 0 | "existing raster."); |
347 | 0 | return false; |
348 | 0 | } |
349 | 0 | aosOptions.AddString("-ot"); |
350 | 0 | aosOptions.AddString(m_outputType.c_str()); |
351 | 0 | } |
352 | | |
353 | 0 | if (!m_optimization.empty()) |
354 | 0 | { |
355 | 0 | aosOptions.AddString("-optim"); |
356 | 0 | aosOptions.AddString(m_optimization.c_str()); |
357 | 0 | } |
358 | |
|
359 | 0 | bool bOK = false; |
360 | 0 | std::unique_ptr<GDALRasterizeOptions, decltype(&GDALRasterizeOptionsFree)> |
361 | 0 | psOptions{GDALRasterizeOptionsNew(aosOptions.List(), nullptr), |
362 | 0 | GDALRasterizeOptionsFree}; |
363 | 0 | if (psOptions) |
364 | 0 | { |
365 | 0 | GDALRasterizeOptionsSetProgress(psOptions.get(), ctxt.m_pfnProgress, |
366 | 0 | ctxt.m_pProgressData); |
367 | |
|
368 | 0 | GDALDatasetH hDstDS = |
369 | 0 | GDALDataset::ToHandle(m_outputDataset.GetDatasetRef()); |
370 | |
|
371 | 0 | GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS); |
372 | |
|
373 | 0 | auto poRetDS = GDALDataset::FromHandle(GDALRasterize( |
374 | 0 | outputFilename.c_str(), hDstDS, hSrcDS, psOptions.get(), nullptr)); |
375 | 0 | bOK = poRetDS != nullptr; |
376 | |
|
377 | 0 | if (!hDstDS) |
378 | 0 | { |
379 | 0 | if (!m_standaloneStep && poRetDS) |
380 | 0 | { |
381 | 0 | VSIUnlink(outputFilename.c_str()); |
382 | 0 | poRetDS->MarkSuppressOnClose(); |
383 | 0 | } |
384 | |
|
385 | 0 | m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS)); |
386 | 0 | } |
387 | 0 | } |
388 | |
|
389 | 0 | return bOK; |
390 | 0 | } |
391 | | |
392 | | /************************************************************************/ |
393 | | /* GDALVectorRasterizeAlgorithm::RunImpl() */ |
394 | | /************************************************************************/ |
395 | | |
396 | | bool GDALVectorRasterizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress, |
397 | | void *pProgressData) |
398 | 0 | { |
399 | 0 | GDALPipelineStepRunContext stepCtxt; |
400 | 0 | stepCtxt.m_pfnProgress = pfnProgress; |
401 | 0 | stepCtxt.m_pProgressData = pProgressData; |
402 | 0 | return RunPreStepPipelineValidations() && RunStep(stepCtxt); |
403 | 0 | } |
404 | | |
405 | | GDALVectorRasterizeAlgorithmStandalone:: |
406 | 0 | ~GDALVectorRasterizeAlgorithmStandalone() = default; |
407 | | |
408 | | //! @endcond |