Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster footprint" subcommand
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_raster_footprint.h"
14 :
15 : #include "cpl_conv.h"
16 :
17 : #include "gdal_priv.h"
18 : #include "gdal_utils.h"
19 :
20 : //! @cond Doxygen_Suppress
21 :
22 : #ifndef _
23 : #define _(x) (x)
24 : #endif
25 :
26 : /************************************************************************/
27 : /* GDALRasterFootprintAlgorithm::GDALRasterFootprintAlgorithm() */
28 : /************************************************************************/
29 :
30 37 : GDALRasterFootprintAlgorithm::GDALRasterFootprintAlgorithm()
31 37 : : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
32 : {
33 37 : AddProgressArg();
34 :
35 37 : AddOpenOptionsArg(&m_openOptions);
36 37 : AddInputFormatsArg(&m_inputFormats)
37 74 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
38 37 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
39 :
40 37 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
41 37 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
42 : AddOutputFormatArg(&m_format, /* bStreamAllowed = */ false,
43 37 : /* bGDALGAllowed = */ false)
44 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
45 111 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
46 74 : AddArg("output-layer", 0, _("Output layer name"), &m_outputLayerName)
47 37 : .SetDefault(m_outputLayerName);
48 37 : AddCreationOptionsArg(&m_creationOptions);
49 37 : AddLayerCreationOptionsArg(&m_layerCreationOptions);
50 37 : AddAppendUpdateArg(&m_append);
51 37 : AddOverwriteArg(&m_overwrite);
52 :
53 37 : AddBandArg(&m_bands);
54 : AddArg("combine-bands", 0,
55 : _("Defines how the mask bands of the selected bands are combined to "
56 : "generate a single mask band, before being vectorized."),
57 74 : &m_combineBands)
58 37 : .SetChoices("union", "intersection")
59 37 : .SetDefault(m_combineBands);
60 : AddArg("overview", 0, _("Which overview level of source file must be used"),
61 74 : &m_overview)
62 74 : .SetMutualExclusionGroup("overview-srcnodata")
63 37 : .SetMinValueIncluded(0);
64 : AddArg("src-nodata", 0, _("Set nodata values for input bands."),
65 74 : &m_srcNoData)
66 37 : .SetMinCount(1)
67 37 : .SetRepeatedArgAllowed(false)
68 37 : .SetMutualExclusionGroup("overview-srcnodata");
69 : AddArg("coordinate-system", 0, _("Target coordinate system"),
70 74 : &m_coordinateSystem)
71 37 : .SetChoices("georeferenced", "pixel");
72 74 : AddArg("dst-crs", 0, _("Destination CRS"), &m_dstCrs)
73 74 : .SetIsCRSArg()
74 37 : .AddHiddenAlias("t_srs");
75 : AddArg("split-multipolygons", 0,
76 : _("Whether to split multipolygons as several features each with one "
77 : "single polygon"),
78 37 : &m_splitMultiPolygons);
79 : AddArg("convex-hull", 0,
80 : _("Whether to compute the convex hull of the footprint"),
81 37 : &m_convexHull);
82 : AddArg("densify-distance", 0,
83 : _("Maximum distance between 2 consecutive points of the output "
84 : "geometry."),
85 74 : &m_densifyVal)
86 37 : .SetMinValueExcluded(0);
87 : AddArg(
88 : "simplify-tolerance", 0,
89 : _("Tolerance used to merge consecutive points of the output geometry."),
90 74 : &m_simplifyVal)
91 37 : .SetMinValueExcluded(0);
92 : AddArg("min-ring-area", 0, _("Minimum value for the area of a ring"),
93 74 : &m_minRingArea)
94 37 : .SetMinValueIncluded(0);
95 : AddArg("max-points", 0,
96 74 : _("Maximum number of points of each output geometry"), &m_maxPoints)
97 37 : .SetDefault(m_maxPoints)
98 : .AddValidationAction(
99 7 : [this]()
100 : {
101 2 : if (m_maxPoints != "unlimited")
102 : {
103 2 : char *endptr = nullptr;
104 : const auto nVal =
105 2 : std::strtoll(m_maxPoints.c_str(), &endptr, 10);
106 3 : if (nVal < 4 ||
107 1 : endptr != m_maxPoints.c_str() + m_maxPoints.size())
108 : {
109 1 : ReportError(
110 : CE_Failure, CPLE_IllegalArg,
111 : "Value of 'max-points' should be a positive "
112 : "integer greater or equal to 4, or 'unlimited'");
113 1 : return false;
114 : }
115 : }
116 1 : return true;
117 37 : });
118 : AddArg("location-field", 0,
119 : _("Name of the field where the path of the input dataset will be "
120 : "stored."),
121 74 : &m_locationField)
122 37 : .SetDefault(m_locationField)
123 37 : .SetMutualExclusionGroup("location");
124 : AddArg("no-location-field", 0,
125 : _("Disable creating a field with the path of the input dataset"),
126 74 : &m_noLocation)
127 37 : .SetMutualExclusionGroup("location");
128 : AddArg("absolute-path", 0,
129 : _("Whether the path to the input dataset should be stored as an "
130 : "absolute path"),
131 37 : &m_writeAbsolutePaths);
132 :
133 37 : AddValidationAction(
134 71 : [this]
135 : {
136 33 : if (auto poSrcDS = m_inputDataset.GetDatasetRef())
137 : {
138 : const int nOvrCount =
139 33 : poSrcDS->GetRasterBand(1)->GetOverviewCount();
140 36 : if (m_overview >= 0 && poSrcDS->GetRasterCount() > 0 &&
141 3 : m_overview >= nOvrCount)
142 : {
143 2 : if (nOvrCount == 0)
144 : {
145 1 : ReportError(
146 : CE_Failure, CPLE_IllegalArg,
147 : "Source dataset has no overviews. "
148 : "Argument 'overview' should not be specified.");
149 : }
150 : else
151 : {
152 1 : ReportError(
153 : CE_Failure, CPLE_IllegalArg,
154 : "Source dataset has only %d overview levels. "
155 : "'overview' "
156 : "value should be strictly lower than this number.",
157 : nOvrCount);
158 : }
159 2 : return false;
160 : }
161 : }
162 31 : return true;
163 : });
164 37 : }
165 :
166 : /************************************************************************/
167 : /* GDALRasterFootprintAlgorithm::RunImpl() */
168 : /************************************************************************/
169 :
170 30 : bool GDALRasterFootprintAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
171 : void *pProgressData)
172 : {
173 60 : CPLStringList aosOptions;
174 36 : for (int band : m_bands)
175 : {
176 6 : aosOptions.push_back("-b");
177 6 : aosOptions.push_back(CPLSPrintf("%d", band));
178 : }
179 :
180 30 : aosOptions.push_back("-combine_bands");
181 30 : aosOptions.push_back(m_combineBands);
182 :
183 30 : if (m_overview >= 0)
184 : {
185 1 : aosOptions.push_back("-ovr");
186 1 : aosOptions.push_back(CPLSPrintf("%d", m_overview));
187 : }
188 :
189 30 : if (!m_srcNoData.empty())
190 : {
191 2 : aosOptions.push_back("-srcnodata");
192 4 : std::string s;
193 5 : for (double v : m_srcNoData)
194 : {
195 3 : if (!s.empty())
196 1 : s += " ";
197 3 : s += CPLSPrintf("%.17g", v);
198 : }
199 2 : aosOptions.push_back(s);
200 : }
201 :
202 30 : if (m_coordinateSystem == "pixel")
203 : {
204 1 : aosOptions.push_back("-t_cs");
205 1 : aosOptions.push_back("pixel");
206 : }
207 29 : else if (m_coordinateSystem == "georeferenced")
208 : {
209 1 : aosOptions.push_back("-t_cs");
210 1 : aosOptions.push_back("georef");
211 : }
212 :
213 30 : if (!m_dstCrs.empty())
214 : {
215 1 : aosOptions.push_back("-t_srs");
216 1 : aosOptions.push_back(m_dstCrs);
217 : }
218 :
219 30 : if (!m_format.empty())
220 : {
221 25 : aosOptions.push_back("-of");
222 25 : aosOptions.push_back(m_format.c_str());
223 : }
224 :
225 31 : for (const auto &co : m_creationOptions)
226 : {
227 1 : aosOptions.push_back("-dsco");
228 1 : aosOptions.push_back(co.c_str());
229 : }
230 :
231 31 : for (const auto &co : m_layerCreationOptions)
232 : {
233 1 : aosOptions.push_back("-lco");
234 1 : aosOptions.push_back(co.c_str());
235 : }
236 :
237 30 : if (GetArg("output-layer")->IsExplicitlySet())
238 : {
239 5 : aosOptions.push_back("-lyr_name");
240 5 : aosOptions.push_back(m_outputLayerName.c_str());
241 : }
242 :
243 30 : if (m_splitMultiPolygons)
244 1 : aosOptions.push_back("-split_polys");
245 :
246 30 : if (m_convexHull)
247 1 : aosOptions.push_back("-convex_hull");
248 :
249 30 : if (m_densifyVal > 0)
250 : {
251 1 : aosOptions.push_back("-densify");
252 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_densifyVal));
253 : }
254 :
255 30 : if (m_simplifyVal > 0)
256 : {
257 1 : aosOptions.push_back("-simplify");
258 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_simplifyVal));
259 : }
260 :
261 30 : aosOptions.push_back("-min_ring_area");
262 30 : aosOptions.push_back(CPLSPrintf("%.17g", m_minRingArea));
263 :
264 30 : aosOptions.push_back("-max_points");
265 30 : aosOptions.push_back(m_maxPoints);
266 :
267 30 : if (m_noLocation)
268 : {
269 1 : aosOptions.push_back("-no_location");
270 : }
271 : else
272 : {
273 29 : aosOptions.push_back("-location_field_name");
274 29 : aosOptions.push_back(m_locationField);
275 :
276 29 : if (m_writeAbsolutePaths)
277 1 : aosOptions.push_back("-write_absolute_path");
278 : }
279 :
280 30 : bool bOK = false;
281 : std::unique_ptr<GDALFootprintOptions, decltype(&GDALFootprintOptionsFree)>
282 : psOptions{GDALFootprintOptionsNew(aosOptions.List(), nullptr),
283 30 : GDALFootprintOptionsFree};
284 30 : if (psOptions)
285 : {
286 30 : GDALFootprintOptionsSetProgress(psOptions.get(), pfnProgress,
287 : pProgressData);
288 :
289 : GDALDatasetH hSrcDS =
290 30 : GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
291 : GDALDatasetH hDstDS =
292 30 : GDALDataset::ToHandle(m_outputDataset.GetDatasetRef());
293 30 : auto poRetDS = GDALDataset::FromHandle(
294 30 : GDALFootprint(m_outputDataset.GetName().c_str(), hDstDS, hSrcDS,
295 30 : psOptions.get(), nullptr));
296 30 : bOK = poRetDS != nullptr;
297 30 : if (bOK && !hDstDS)
298 : {
299 27 : m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
300 : }
301 : }
302 :
303 60 : return bOK;
304 : }
305 :
306 : //! @endcond
|