Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster buildvrt" subcommand
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_raster_buildvrt.h"
14 :
15 : #include "cpl_conv.h"
16 : #include "cpl_vsi_virtual.h"
17 :
18 : #include "gdal_priv.h"
19 : #include "gdal_utils.h"
20 :
21 : //! @cond Doxygen_Suppress
22 :
23 : #ifndef _
24 : #define _(x) (x)
25 : #endif
26 :
27 : /************************************************************************/
28 : /* GDALRasterBuildVRTAlgorithm::GDALRasterBuildVRTAlgorithm() */
29 : /************************************************************************/
30 :
31 24 : GDALRasterBuildVRTAlgorithm::GDALRasterBuildVRTAlgorithm()
32 24 : : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
33 : {
34 24 : AddProgressArg();
35 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
36 : _("Input raster datasets (or specify a @<filename> to point to a "
37 : "file containing filenames)"),
38 48 : &m_inputDatasets, GDAL_OF_RASTER)
39 24 : .SetPositional()
40 24 : .SetMinCount(1)
41 24 : .SetAutoOpenDataset(false)
42 24 : .SetMetaVar("INPUTS");
43 24 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
44 24 : AddCreationOptionsArg(&m_creationOptions);
45 24 : AddArg("band", 'b', _("Specify input band(s) number."), &m_bands);
46 : AddArg("separate", 0, _("Place each input file into a separate band."),
47 24 : &m_separate);
48 24 : AddOverwriteArg(&m_overwrite);
49 : {
50 : auto &arg = AddArg("resolution", 0,
51 : _("Target resolution (in destination CRS units)"),
52 48 : &m_resolution)
53 24 : .SetMetaVar("<xres>,<yres>|average|highest|lowest");
54 : arg.AddValidationAction(
55 10 : [this, &arg]()
56 : {
57 14 : const std::string val = arg.Get<std::string>();
58 7 : if (val != "average" && val != "highest" && val != "lowest")
59 : {
60 : const auto aosTokens =
61 5 : CPLStringList(CSLTokenizeString2(val.c_str(), ",", 0));
62 5 : if (aosTokens.size() != 2 ||
63 3 : CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING ||
64 3 : CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING ||
65 10 : CPLAtof(aosTokens[0]) <= 0 ||
66 2 : CPLAtof(aosTokens[1]) <= 0)
67 : {
68 3 : ReportError(CE_Failure, CPLE_AppDefined,
69 : "resolution: two comma separated positive "
70 : "values should be provided, or 'average', "
71 : "'highest' or 'lowest'");
72 3 : return false;
73 : }
74 : }
75 4 : return true;
76 24 : });
77 : }
78 : AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
79 24 : "destination CRS units)"));
80 : AddArg("target-aligned-pixels", 0,
81 : _("Round target extent to target resolution"),
82 48 : &m_targetAlignedPixels)
83 24 : .AddHiddenAlias("tap");
84 : AddArg("srcnodata", 0, _("Set nodata values for input bands."),
85 48 : &m_srcNoData)
86 24 : .SetMinCount(1)
87 24 : .SetRepeatedArgAllowed(false);
88 : AddArg("vrtnodata", 0, _("Set nodata values at the VRT band level."),
89 48 : &m_vrtNoData)
90 24 : .SetMinCount(1)
91 24 : .SetRepeatedArgAllowed(false);
92 : AddArg("hidenodata", 0, _("Makes the VRT band not report the NoData."),
93 24 : &m_hideNoData);
94 : AddArg("addalpha", 0,
95 : _("Adds an alpha mask band to the VRT when the source raster have "
96 : "none."),
97 24 : &m_addAlpha);
98 24 : }
99 :
100 : /************************************************************************/
101 : /* GDALRasterBuildVRTAlgorithm::RunImpl() */
102 : /************************************************************************/
103 :
104 21 : bool GDALRasterBuildVRTAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
105 : void *pProgressData)
106 : {
107 21 : if (m_outputDataset.GetDatasetRef())
108 : {
109 1 : ReportError(CE_Failure, CPLE_NotSupported,
110 : "gdal raster buildvrt does not support outputting to an "
111 : "already opened output dataset");
112 1 : return false;
113 : }
114 :
115 40 : std::vector<GDALDatasetH> ahInputDatasets;
116 40 : CPLStringList aosInputDatasetNames;
117 20 : bool foundByRef = false;
118 20 : bool foundByName = false;
119 46 : for (auto &ds : m_inputDatasets)
120 : {
121 27 : if (ds.GetDatasetRef())
122 : {
123 14 : foundByRef = true;
124 14 : ahInputDatasets.push_back(
125 14 : GDALDataset::ToHandle(ds.GetDatasetRef()));
126 : }
127 13 : else if (!ds.GetName().empty())
128 : {
129 13 : foundByName = true;
130 13 : if (ds.GetName()[0] == '@')
131 : {
132 : auto f = VSIVirtualHandleUniquePtr(
133 2 : VSIFOpenL(ds.GetName().c_str() + 1, "r"));
134 2 : if (!f)
135 : {
136 1 : ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
137 1 : ds.GetName().c_str() + 1);
138 1 : return false;
139 : }
140 2 : while (const char *filename = CPLReadLineL(f.get()))
141 : {
142 1 : aosInputDatasetNames.push_back(filename);
143 1 : }
144 : }
145 11 : else if (ds.GetName().find_first_of("*?[") != std::string::npos)
146 : {
147 1 : CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
148 2 : pfnProgress, pProgressData));
149 2 : for (const char *pszStr : aosMatches)
150 : {
151 1 : aosInputDatasetNames.push_back(pszStr);
152 : }
153 : }
154 : else
155 : {
156 10 : aosInputDatasetNames.push_back(ds.GetName().c_str());
157 : }
158 : }
159 : }
160 19 : if (foundByName && foundByRef)
161 : {
162 0 : ReportError(CE_Failure, CPLE_NotSupported,
163 : "Input datasets should be provided either all by reference "
164 : "or all by name");
165 0 : return false;
166 : }
167 :
168 : VSIStatBufL sStat;
169 21 : if (!m_overwrite && !m_outputDataset.GetName().empty() &&
170 3 : (VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
171 20 : std::unique_ptr<GDALDataset>(
172 2 : GDALDataset::Open(m_outputDataset.GetName().c_str()))))
173 : {
174 1 : ReportError(CE_Failure, CPLE_AppDefined,
175 : "File '%s' already exists. Specify the --overwrite "
176 : "option to overwrite it.",
177 1 : m_outputDataset.GetName().c_str());
178 1 : return false;
179 : }
180 :
181 36 : CPLStringList aosOptions;
182 18 : if (!m_resolution.empty())
183 : {
184 : const auto aosTokens =
185 8 : CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
186 4 : if (aosTokens.size() == 2)
187 : {
188 2 : aosOptions.push_back("-tr");
189 2 : aosOptions.push_back(aosTokens[0]);
190 2 : aosOptions.push_back(aosTokens[1]);
191 : }
192 : else
193 : {
194 2 : aosOptions.push_back("-resolution");
195 2 : aosOptions.push_back(m_resolution);
196 : }
197 : }
198 18 : if (!m_bbox.empty())
199 : {
200 1 : aosOptions.push_back("-te");
201 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
202 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
203 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
204 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
205 : }
206 18 : if (m_targetAlignedPixels)
207 : {
208 1 : aosOptions.push_back("-tap");
209 : }
210 18 : if (!m_srcNoData.empty())
211 : {
212 2 : aosOptions.push_back("-srcnodata");
213 4 : std::string s;
214 4 : for (double v : m_srcNoData)
215 : {
216 2 : if (!s.empty())
217 0 : s += " ";
218 2 : s += CPLSPrintf("%.17g", v);
219 : }
220 2 : aosOptions.push_back(s);
221 : }
222 18 : if (!m_vrtNoData.empty())
223 : {
224 2 : aosOptions.push_back("-vrtnodata");
225 4 : std::string s;
226 4 : for (double v : m_vrtNoData)
227 : {
228 2 : if (!s.empty())
229 0 : s += " ";
230 2 : s += CPLSPrintf("%.17g", v);
231 : }
232 2 : aosOptions.push_back(s);
233 : }
234 18 : if (m_separate)
235 : {
236 1 : aosOptions.push_back("-separate");
237 : }
238 20 : for (const auto &co : m_creationOptions)
239 : {
240 2 : aosOptions.push_back("-co");
241 2 : aosOptions.push_back(co);
242 : }
243 20 : for (const int b : m_bands)
244 : {
245 2 : aosOptions.push_back("-b");
246 2 : aosOptions.push_back(CPLSPrintf("%d", b));
247 : }
248 18 : if (m_addAlpha)
249 : {
250 1 : aosOptions.push_back("-addalpha");
251 : }
252 18 : if (m_hideNoData)
253 : {
254 1 : aosOptions.push_back("-hidenodata");
255 : }
256 :
257 : GDALBuildVRTOptions *psOptions =
258 18 : GDALBuildVRTOptionsNew(aosOptions.List(), nullptr);
259 18 : GDALBuildVRTOptionsSetProgress(psOptions, pfnProgress, pProgressData);
260 :
261 : auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
262 18 : GDALBuildVRT(m_outputDataset.GetName().c_str(),
263 10 : foundByName ? aosInputDatasetNames.size()
264 8 : : static_cast<int>(m_inputDatasets.size()),
265 26 : ahInputDatasets.empty() ? nullptr : ahInputDatasets.data(),
266 70 : aosInputDatasetNames.List(), psOptions, nullptr)));
267 18 : GDALBuildVRTOptionsFree(psOptions);
268 18 : const bool bOK = poOutDS != nullptr;
269 18 : if (bOK)
270 : {
271 18 : m_outputDataset.Set(std::move(poOutDS));
272 : }
273 :
274 18 : return bOK;
275 : }
276 :
277 : //! @endcond
|