Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster contour" 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_raster_contour.h"
16 :
17 : #include "cpl_conv.h"
18 : #include "gdal_priv.h"
19 : #include "gdal_utils.h"
20 : #include "gdal_alg.h"
21 : #include "gdal_utils_priv.h"
22 :
23 : //! @cond Doxygen_Suppress
24 :
25 : #ifndef _
26 : #define _(x) (x)
27 : #endif
28 :
29 : /************************************************************************/
30 : /* GDALRasterContourAlgorithm::GDALRasterContourAlgorithm() */
31 : /************************************************************************/
32 :
33 18 : GDALRasterContourAlgorithm::GDALRasterContourAlgorithm()
34 : : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL), m_outputLayerName("contour"),
35 18 : m_elevAttributeName(""), m_amin(""), m_amax(""), m_levels{}
36 : {
37 :
38 18 : AddProgressArg();
39 18 : AddOutputFormatArg(&m_outputFormat)
40 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
41 54 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATECOPY});
42 18 : AddOpenOptionsArg(&m_openOptions);
43 18 : AddInputFormatsArg(&m_inputFormats)
44 36 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
45 18 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
46 18 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
47 18 : AddCreationOptionsArg(&m_creationOptions);
48 :
49 : // gdal_contour specific options
50 18 : AddArg("band", 'b', _("Specify input band number"), &m_band).SetDefault(1);
51 18 : AddLayerNameArg(&m_outputLayerName).AddAlias("nln");
52 : AddArg("elevation-name", 0, _("Name of the elevation field"),
53 18 : &m_elevAttributeName);
54 18 : AddArg("min-name", 0, _("Name of the minimum elevation field"), &m_amin);
55 18 : AddArg("max-name", 0, _("Name of the maximum elevation field"), &m_amax);
56 18 : AddArg("3d", 0, _("Force production of 3D vectors instead of 2D"), &m_3d);
57 :
58 : AddArg("srcnodata", 0, _("Input pixel value to treat as 'nodata'"),
59 18 : &m_sNodata);
60 36 : AddArg("interval", 0, _("Elevation interval between contours"), &m_interval)
61 18 : .SetMutualExclusionGroup("levels");
62 36 : AddArg("levels", 0, _("List of contour levels"), &m_levels)
63 18 : .SetMutualExclusionGroup("levels");
64 : AddArg("exp-base", 'e', _("Base for exponential contour level generation"),
65 36 : &m_expBase)
66 18 : .SetMutualExclusionGroup("levels");
67 36 : AddArg("offset", 0, _("Offset to apply to contour levels"), &m_offset)
68 18 : .AddAlias("off");
69 : AddArg("polygonize", 'p', _("Create polygons instead of lines"),
70 18 : &m_polygonize);
71 : AddArg("group-transactions", 0,
72 : _("Group n features per transaction (default 100 000)"),
73 18 : &m_groupTransactions);
74 18 : AddOverwriteArg(&m_overwrite);
75 18 : }
76 :
77 : /************************************************************************/
78 : /* GDALRasterContourAlgorithm::RunImpl() */
79 : /************************************************************************/
80 :
81 14 : bool GDALRasterContourAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
82 : void *pProgressData)
83 : {
84 :
85 14 : CPLErrorReset();
86 :
87 14 : CPLAssert(m_inputDataset.GetDatasetRef());
88 14 : if (m_outputDataset.GetDatasetRef())
89 : {
90 0 : CPLError(CE_Failure, CPLE_NotSupported,
91 : "gdal raster contour does not support outputting to an "
92 : "already opened output dataset");
93 0 : return false;
94 : }
95 :
96 28 : CPLStringList aosOptions;
97 14 : if (!m_outputFormat.empty())
98 : {
99 0 : aosOptions.AddString("-of");
100 0 : aosOptions.AddString(m_outputFormat);
101 : }
102 :
103 14 : for (const auto &co : m_creationOptions)
104 : {
105 0 : aosOptions.AddString("-co");
106 0 : aosOptions.AddString(co);
107 : }
108 14 : if (m_band > 0)
109 : {
110 14 : aosOptions.AddString("-b");
111 14 : aosOptions.AddString(CPLSPrintf("%d", m_band));
112 : }
113 14 : if (!m_elevAttributeName.empty())
114 : {
115 5 : aosOptions.AddString("-a");
116 5 : aosOptions.AddString(m_elevAttributeName);
117 : }
118 14 : if (!m_amin.empty())
119 : {
120 7 : aosOptions.AddString("-amin");
121 7 : aosOptions.AddString(m_amin);
122 : }
123 14 : if (!m_amax.empty())
124 : {
125 7 : aosOptions.AddString("-amax");
126 7 : aosOptions.AddString(m_amax);
127 : }
128 14 : if (m_3d)
129 : {
130 0 : aosOptions.AddString("-3d");
131 : }
132 14 : if (!std::isnan(m_sNodata))
133 : {
134 1 : aosOptions.AddString("-snodata");
135 1 : aosOptions.AddString(CPLSPrintf("%.16g", m_sNodata));
136 : }
137 14 : if (m_levels.size() > 0)
138 : {
139 12 : for (const auto &level : m_levels)
140 : {
141 8 : aosOptions.AddString("-fl");
142 8 : aosOptions.AddString(level);
143 : }
144 : }
145 14 : if (!std::isnan(m_interval))
146 : {
147 8 : aosOptions.AddString("-i");
148 8 : aosOptions.AddString(CPLSPrintf("%.16g", m_interval));
149 : }
150 14 : if (m_expBase > 0)
151 : {
152 1 : aosOptions.AddString("-e");
153 1 : aosOptions.AddString(CPLSPrintf("%d", m_expBase));
154 : }
155 14 : if (!std::isnan(m_offset))
156 : {
157 1 : aosOptions.AddString("-off");
158 1 : aosOptions.AddString(CPLSPrintf("%.16g", m_offset));
159 : }
160 14 : if (m_polygonize)
161 : {
162 6 : aosOptions.AddString("-p");
163 : }
164 14 : if (!m_outputLayerName.empty())
165 : {
166 14 : aosOptions.AddString("-nln");
167 14 : aosOptions.AddString(m_outputLayerName);
168 : }
169 :
170 : VSIStatBufL sStat;
171 27 : if (!m_overwrite && !m_outputDataset.GetName().empty() &&
172 25 : (VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
173 26 : std::unique_ptr<GDALDataset>(
174 24 : GDALDataset::Open(m_outputDataset.GetName().c_str()))))
175 : {
176 1 : ReportError(CE_Failure, CPLE_AppDefined,
177 : "File '%s' already exists. Specify the --overwrite "
178 : "option to overwrite it.",
179 1 : m_outputDataset.GetName().c_str());
180 1 : return false;
181 : }
182 :
183 : // Check that one of --interval, --levels, --exp-base is specified
184 13 : if (m_levels.size() == 0 && std::isnan(m_interval) && m_expBase == 0)
185 : {
186 1 : ReportError(
187 : CE_Failure, CPLE_AppDefined,
188 : "One of 'interval', 'levels', 'exp-base' must be specified.");
189 1 : return false;
190 : }
191 :
192 : // Check that interval is not negative
193 12 : if (!std::isnan(m_interval) && m_interval < 0)
194 : {
195 1 : ReportError(CE_Failure, CPLE_AppDefined,
196 : "Interval must be a positive number.");
197 1 : return false;
198 : }
199 :
200 11 : aosOptions.AddString(m_inputDataset.GetName());
201 11 : aosOptions.AddString(m_outputDataset.GetName());
202 :
203 22 : GDALContourOptionsForBinary optionsForBinary;
204 : std::unique_ptr<GDALContourOptions, decltype(&GDALContourOptionsFree)>
205 : psOptions{GDALContourOptionsNew(aosOptions.List(), &optionsForBinary),
206 22 : GDALContourOptionsFree};
207 :
208 11 : if (!psOptions)
209 : {
210 0 : return false;
211 : }
212 :
213 11 : GDALDatasetH hSrcDS{m_inputDataset.GetDatasetRef()};
214 11 : GDALRasterBandH hBand{nullptr};
215 11 : GDALDatasetH hDstDS{m_outputDataset.GetDatasetRef()};
216 11 : OGRLayerH hLayer{nullptr};
217 11 : char **papszStringOptions = nullptr;
218 :
219 : CPLErr eErr =
220 11 : GDALContourProcessOptions(psOptions.get(), &papszStringOptions, &hSrcDS,
221 : &hBand, &hDstDS, &hLayer);
222 :
223 11 : if (eErr == CE_None)
224 : {
225 11 : eErr = GDALContourGenerateEx(hBand, hLayer, papszStringOptions,
226 : pfnProgress, pProgressData);
227 : }
228 :
229 11 : CSLDestroy(papszStringOptions);
230 :
231 11 : auto poDstDS = GDALDataset::FromHandle(hDstDS);
232 11 : m_outputDataset.Set(std::unique_ptr<GDALDataset>(poDstDS));
233 :
234 11 : return eErr == CE_None;
235 : }
236 :
237 : //! @endcond
|