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