Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster polygonize" 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_polygonize.h"
14 :
15 : #include "cpl_conv.h"
16 : #include "gdal_priv.h"
17 : #include "gdal_alg.h"
18 : #include "ogrsf_frmts.h"
19 :
20 : //! @cond Doxygen_Suppress
21 :
22 : #ifndef _
23 : #define _(x) (x)
24 : #endif
25 :
26 : /************************************************************************/
27 : /* GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm() */
28 : /************************************************************************/
29 :
30 27 : GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm(
31 27 : bool standaloneStep)
32 : : GDALPipelineStepAlgorithm(
33 : NAME, DESCRIPTION, HELP_URL,
34 0 : ConstructorOptions()
35 27 : .SetStandaloneStep(standaloneStep)
36 54 : .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
37 : {
38 27 : m_outputLayerName = "polygonize";
39 :
40 27 : AddProgressArg();
41 27 : if (standaloneStep)
42 : {
43 20 : AddOutputFormatArg(&m_format).AddMetadataItem(
44 60 : GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
45 20 : AddOpenOptionsArg(&m_openOptions);
46 20 : AddInputFormatsArg(&m_inputFormats)
47 40 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
48 20 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
49 20 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
50 20 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
51 20 : AddCreationOptionsArg(&m_creationOptions);
52 20 : AddLayerCreationOptionsArg(&m_layerCreationOptions);
53 20 : AddOverwriteArg(&m_overwrite);
54 20 : AddUpdateArg(&m_update);
55 20 : AddOverwriteLayerArg(&m_overwriteLayer);
56 20 : AddAppendLayerArg(&m_appendLayer);
57 20 : AddLayerNameArg(&m_outputLayerName)
58 40 : .AddAlias("nln")
59 20 : .SetDefault(m_outputLayerName);
60 : }
61 :
62 : // gdal_polygonize specific options
63 27 : AddBandArg(&m_band).SetDefault(m_band);
64 : AddArg("attribute-name", 0, _("Name of the field with the pixel value"),
65 54 : &m_attributeName)
66 27 : .SetDefault(m_attributeName);
67 :
68 : AddArg("connect-diagonal-pixels", 'c',
69 54 : _("Consider diagonal pixels as connected"), &m_connectDiagonalPixels)
70 27 : .SetDefault(m_connectDiagonalPixels);
71 27 : }
72 :
73 : /************************************************************************/
74 : /* GDALRasterPolygonizeAlgorithm::RunImpl() */
75 : /************************************************************************/
76 :
77 15 : bool GDALRasterPolygonizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
78 : void *pProgressData)
79 : {
80 15 : GDALPipelineStepRunContext stepCtxt;
81 15 : stepCtxt.m_pfnProgress = pfnProgress;
82 15 : stepCtxt.m_pProgressData = pProgressData;
83 15 : return RunPreStepPipelineValidations() && RunStep(stepCtxt);
84 : }
85 :
86 : /************************************************************************/
87 : /* GDALRasterPolygonizeAlgorithm::RunStep() */
88 : /************************************************************************/
89 :
90 16 : bool GDALRasterPolygonizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
91 : {
92 16 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
93 16 : CPLAssert(poSrcDS);
94 :
95 16 : GDALDataset *poDstDS = m_outputDataset.GetDatasetRef();
96 16 : std::unique_ptr<GDALDataset> poRetDS;
97 32 : std::string outputFilename = m_outputDataset.GetName();
98 16 : if (!poDstDS)
99 : {
100 11 : if (m_standaloneStep)
101 : {
102 10 : if (m_format.empty())
103 : {
104 : const auto aosFormats =
105 : CPLStringList(GDALGetOutputDriversForDatasetName(
106 5 : m_outputDataset.GetName().c_str(), GDAL_OF_VECTOR,
107 : /* bSingleMatch = */ true,
108 5 : /* bWarn = */ true));
109 5 : if (aosFormats.size() != 1)
110 : {
111 1 : ReportError(CE_Failure, CPLE_AppDefined,
112 : "Cannot guess driver for %s",
113 1 : m_outputDataset.GetName().c_str());
114 1 : return false;
115 : }
116 4 : m_format = aosFormats[0];
117 : }
118 : }
119 : else
120 : {
121 1 : if (GetGDALDriverManager()->GetDriverByName("GPKG"))
122 : {
123 : outputFilename =
124 1 : CPLGenerateTempFilenameSafe("_polygonize.gpkg");
125 1 : m_format = "GPKG";
126 : }
127 : else
128 0 : m_format = "MEM";
129 : }
130 :
131 : auto poDriver =
132 10 : GetGDALDriverManager()->GetDriverByName(m_format.c_str());
133 10 : if (!poDriver)
134 : {
135 : // shouldn't happen given checks done in GDALAlgorithm
136 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
137 : m_format.c_str());
138 0 : return false;
139 : }
140 :
141 10 : poRetDS.reset(
142 : poDriver->Create(outputFilename.c_str(), 0, 0, 0, GDT_Unknown,
143 20 : CPLStringList(m_creationOptions).List()));
144 10 : if (!poRetDS)
145 1 : return false;
146 :
147 9 : if (!m_standaloneStep && !outputFilename.empty())
148 1 : poRetDS->MarkSuppressOnClose();
149 :
150 9 : poDstDS = poRetDS.get();
151 : }
152 :
153 14 : auto poDstDriver = poDstDS->GetDriver();
154 14 : if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") &&
155 33 : EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") &&
156 5 : poDstDS->GetLayerCount() <= 1)
157 : {
158 5 : m_outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription());
159 : }
160 :
161 14 : auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
162 14 : if (poDstLayer)
163 : {
164 4 : if (m_overwriteLayer)
165 : {
166 1 : int iLayer = -1;
167 1 : const int nLayerCount = poDstDS->GetLayerCount();
168 1 : for (iLayer = 0; iLayer < nLayerCount; iLayer++)
169 : {
170 1 : if (poDstDS->GetLayer(iLayer) == poDstLayer)
171 1 : break;
172 : }
173 :
174 1 : if (iLayer < nLayerCount)
175 : {
176 1 : if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
177 : {
178 0 : ReportError(CE_Failure, CPLE_AppDefined,
179 : "Cannot delete layer '%s'",
180 : m_outputLayerName.c_str());
181 0 : return false;
182 : }
183 : }
184 1 : poDstLayer = nullptr;
185 : }
186 3 : else if (!m_appendLayer)
187 : {
188 1 : ReportError(CE_Failure, CPLE_AppDefined,
189 : "Layer '%s' already exists. Specify the "
190 : "--%s option to overwrite it, or --%s "
191 : "to append to it.",
192 : m_outputLayerName.c_str(),
193 : GDAL_ARG_NAME_OVERWRITE_LAYER, GDAL_ARG_NAME_APPEND);
194 1 : return false;
195 : }
196 : }
197 10 : else if (m_appendLayer || m_overwriteLayer)
198 : {
199 1 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'",
200 : m_outputLayerName.c_str());
201 1 : return false;
202 : }
203 :
204 12 : auto poSrcBand = poSrcDS->GetRasterBand(m_band);
205 12 : const auto eDT = poSrcBand->GetRasterDataType();
206 :
207 12 : if (!poDstLayer)
208 : {
209 10 : poDstLayer = poDstDS->CreateLayer(
210 10 : m_outputLayerName.c_str(), poSrcDS->GetSpatialRef(), wkbPolygon,
211 20 : CPLStringList(m_layerCreationOptions).List());
212 10 : if (!poDstLayer)
213 : {
214 1 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot create layer '%s'",
215 : m_outputLayerName.c_str());
216 1 : return false;
217 : }
218 :
219 : OGRFieldDefn oFieldDefn(m_attributeName.c_str(),
220 9 : !GDALDataTypeIsInteger(eDT) ? OFTReal
221 7 : : eDT == GDT_Int64 || eDT == GDT_UInt64
222 15 : ? OFTInteger64
223 17 : : OFTInteger);
224 9 : if (poDstLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
225 : {
226 0 : ReportError(CE_Failure, CPLE_AppDefined,
227 : "Cannot create field '%s' in layer '%s'",
228 : m_attributeName.c_str(), m_outputLayerName.c_str());
229 0 : return false;
230 : }
231 : }
232 :
233 : const int iPixValField =
234 11 : poDstLayer->GetLayerDefn()->GetFieldIndex(m_attributeName.c_str());
235 11 : if (iPixValField < 0)
236 : {
237 1 : ReportError(CE_Failure, CPLE_AppDefined,
238 : "Cannot find field '%s' in layer '%s'",
239 : m_attributeName.c_str(), m_outputLayerName.c_str());
240 1 : return false;
241 : }
242 :
243 10 : CPLStringList aosPolygonizeOptions;
244 10 : if (m_connectDiagonalPixels)
245 : {
246 1 : aosPolygonizeOptions.SetNameValue("8CONNECTED", "8");
247 : }
248 :
249 : bool ret;
250 10 : if (GDALDataTypeIsInteger(eDT))
251 : {
252 18 : ret = GDALPolygonize(GDALRasterBand::ToHandle(poSrcBand),
253 9 : GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
254 : OGRLayer::ToHandle(poDstLayer), iPixValField,
255 : aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
256 : ctxt.m_pProgressData) == CE_None;
257 : }
258 : else
259 : {
260 1 : ret =
261 2 : GDALFPolygonize(GDALRasterBand::ToHandle(poSrcBand),
262 1 : GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
263 : OGRLayer::ToHandle(poDstLayer), iPixValField,
264 : aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
265 : ctxt.m_pProgressData) == CE_None;
266 : }
267 :
268 10 : if (ret && poRetDS)
269 : {
270 8 : if (!m_standaloneStep && !outputFilename.empty())
271 : {
272 1 : ret = poRetDS->FlushCache() == CE_None;
273 1 : VSIUnlink(outputFilename.c_str());
274 : }
275 :
276 8 : m_outputDataset.Set(std::move(poRetDS));
277 : }
278 :
279 10 : return ret;
280 : }
281 :
282 : GDALRasterPolygonizeAlgorithmStandalone::
283 : ~GDALRasterPolygonizeAlgorithmStandalone() = default;
284 :
285 : //! @endcond
|