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 19 : GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm()
31 19 : : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
32 : {
33 :
34 19 : AddProgressArg();
35 19 : AddOutputFormatArg(&m_outputFormat)
36 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
37 57 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
38 19 : AddOpenOptionsArg(&m_openOptions);
39 19 : AddInputFormatsArg(&m_inputFormats)
40 38 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
41 19 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
42 19 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
43 19 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
44 19 : AddCreationOptionsArg(&m_creationOptions);
45 19 : AddLayerCreationOptionsArg(&m_layerCreationOptions);
46 19 : AddOverwriteArg(&m_overwrite);
47 19 : auto &updateArg = AddUpdateArg(&m_update);
48 : AddArg("overwrite-layer", 0,
49 : _("Whether overwriting existing layer is allowed"),
50 38 : &m_overwriteLayer)
51 19 : .SetDefault(false)
52 : .AddValidationAction(
53 1 : [&updateArg]
54 : {
55 1 : updateArg.Set(true);
56 1 : return true;
57 19 : });
58 : AddAppendUpdateArg(&m_appendLayer,
59 19 : _("Whether appending to existing layer is allowed"));
60 :
61 : // gdal_polygonize specific options
62 19 : AddBandArg(&m_band).SetDefault(m_band);
63 19 : AddLayerNameArg(&m_outputLayerName)
64 38 : .AddAlias("nln")
65 19 : .SetDefault(m_outputLayerName);
66 : AddArg("attribute-name", 0, _("Name of the field with the pixel value"),
67 38 : &m_attributeName)
68 19 : .SetDefault(m_attributeName);
69 :
70 : AddArg("connect-diagonal-pixels", 'c',
71 38 : _("Consider diagonal pixels as connected"), &m_connectDiagonalPixels)
72 19 : .SetDefault(m_connectDiagonalPixels);
73 19 : }
74 :
75 : /************************************************************************/
76 : /* GDALRasterPolygonizeAlgorithm::RunImpl() */
77 : /************************************************************************/
78 :
79 15 : bool GDALRasterPolygonizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
80 : void *pProgressData)
81 : {
82 15 : auto poSrcDS = m_inputDataset.GetDatasetRef();
83 15 : CPLAssert(poSrcDS);
84 :
85 15 : GDALDataset *poDstDS = m_outputDataset.GetDatasetRef();
86 15 : std::unique_ptr<GDALDataset> poRetDS;
87 15 : if (!poDstDS)
88 : {
89 10 : if (m_outputFormat.empty())
90 : {
91 : const auto aosFormats =
92 : CPLStringList(GDALGetOutputDriversForDatasetName(
93 5 : m_outputDataset.GetName().c_str(), GDAL_OF_VECTOR,
94 : /* bSingleMatch = */ true,
95 5 : /* bWarn = */ true));
96 5 : if (aosFormats.size() != 1)
97 : {
98 1 : ReportError(CE_Failure, CPLE_AppDefined,
99 : "Cannot guess driver for %s",
100 1 : m_outputDataset.GetName().c_str());
101 1 : return false;
102 : }
103 4 : m_outputFormat = aosFormats[0];
104 : }
105 :
106 : auto poDriver =
107 9 : GetGDALDriverManager()->GetDriverByName(m_outputFormat.c_str());
108 9 : if (!poDriver)
109 : {
110 : // shouldn't happen given checks done in GDALAlgorithm
111 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
112 : m_outputFormat.c_str());
113 0 : return false;
114 : }
115 :
116 9 : poRetDS.reset(poDriver->Create(
117 9 : m_outputDataset.GetName().c_str(), 0, 0, 0, GDT_Unknown,
118 18 : CPLStringList(m_creationOptions).List()));
119 9 : if (!poRetDS)
120 1 : return false;
121 :
122 8 : poDstDS = poRetDS.get();
123 : }
124 :
125 13 : auto poDstDriver = poDstDS->GetDriver();
126 13 : if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") &&
127 31 : EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") &&
128 5 : poDstDS->GetLayerCount() <= 1)
129 : {
130 5 : m_outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription());
131 : }
132 :
133 13 : auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
134 13 : if (poDstLayer)
135 : {
136 4 : if (m_overwriteLayer)
137 : {
138 1 : int iLayer = -1;
139 1 : const int nLayerCount = poDstDS->GetLayerCount();
140 1 : for (iLayer = 0; iLayer < nLayerCount; iLayer++)
141 : {
142 1 : if (poDstDS->GetLayer(iLayer) == poDstLayer)
143 1 : break;
144 : }
145 :
146 1 : if (iLayer < nLayerCount)
147 : {
148 1 : if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
149 : {
150 0 : ReportError(CE_Failure, CPLE_AppDefined,
151 : "Cannot delete layer '%s'",
152 : m_outputLayerName.c_str());
153 0 : return false;
154 : }
155 : }
156 1 : poDstLayer = nullptr;
157 : }
158 3 : else if (!m_appendLayer)
159 : {
160 1 : ReportError(CE_Failure, CPLE_AppDefined,
161 : "Layer '%s' already exists. Specify the "
162 : "--overwrite-layer option to overwrite it, or --append "
163 : "to append to it.",
164 : m_outputLayerName.c_str());
165 1 : return false;
166 : }
167 : }
168 9 : else if (m_appendLayer || m_overwriteLayer)
169 : {
170 1 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'",
171 : m_outputLayerName.c_str());
172 1 : return false;
173 : }
174 :
175 11 : auto poSrcBand = poSrcDS->GetRasterBand(m_band);
176 11 : const auto eDT = poSrcBand->GetRasterDataType();
177 :
178 11 : if (!poDstLayer)
179 : {
180 9 : poDstLayer = poDstDS->CreateLayer(
181 9 : m_outputLayerName.c_str(), poSrcDS->GetSpatialRef(), wkbPolygon,
182 18 : CPLStringList(m_layerCreationOptions).List());
183 9 : if (!poDstLayer)
184 : {
185 1 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot create layer '%s'",
186 : m_outputLayerName.c_str());
187 1 : return false;
188 : }
189 :
190 : OGRFieldDefn oFieldDefn(m_attributeName.c_str(),
191 8 : !GDALDataTypeIsInteger(eDT) ? OFTReal
192 6 : : eDT == GDT_Int64 || eDT == GDT_UInt64
193 13 : ? OFTInteger64
194 15 : : OFTInteger);
195 8 : if (poDstLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
196 : {
197 0 : ReportError(CE_Failure, CPLE_AppDefined,
198 : "Cannot create field '%s' in layer '%s'",
199 : m_attributeName.c_str(), m_outputLayerName.c_str());
200 0 : return false;
201 : }
202 : }
203 :
204 : const int iPixValField =
205 10 : poDstLayer->GetLayerDefn()->GetFieldIndex(m_attributeName.c_str());
206 10 : if (iPixValField < 0)
207 : {
208 1 : ReportError(CE_Failure, CPLE_AppDefined,
209 : "Cannot find field '%s' in layer '%s'",
210 : m_attributeName.c_str(), m_outputLayerName.c_str());
211 1 : return false;
212 : }
213 :
214 9 : CPLStringList aosPolygonizeOptions;
215 9 : if (m_connectDiagonalPixels)
216 : {
217 1 : aosPolygonizeOptions.SetNameValue("8CONNECTED", "8");
218 : }
219 :
220 : bool ret;
221 9 : if (GDALDataTypeIsInteger(eDT))
222 : {
223 16 : ret = GDALPolygonize(GDALRasterBand::ToHandle(poSrcBand),
224 8 : GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
225 : OGRLayer::ToHandle(poDstLayer), iPixValField,
226 : aosPolygonizeOptions.List(), pfnProgress,
227 : pProgressData) == CE_None;
228 : }
229 : else
230 : {
231 1 : ret =
232 2 : GDALFPolygonize(GDALRasterBand::ToHandle(poSrcBand),
233 1 : GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
234 : OGRLayer::ToHandle(poDstLayer), iPixValField,
235 : aosPolygonizeOptions.List(), pfnProgress,
236 : pProgressData) == CE_None;
237 : }
238 :
239 9 : if (ret && poRetDS)
240 : {
241 7 : m_outputDataset.Set(std::move(poRetDS));
242 : }
243 :
244 9 : return ret;
245 : }
246 :
247 : //! @endcond
|