Skip to content

Commit 087abc6

Browse files
authored
Merge pull request #12122 from rouault/gdal_raster_polygonize
Add 'gdal raster polygonize' (port of gdal_polygonize.py)
2 parents 7dce65a + caf5ae4 commit 087abc6

14 files changed

+754
-17
lines changed

apps/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_library(
3030
gdalalg_raster_index.cpp
3131
gdalalg_raster_mosaic.cpp
3232
gdalalg_raster_pipeline.cpp
33+
gdalalg_raster_polygonize.cpp
3334
gdalalg_raster_overview_add.cpp
3435
gdalalg_raster_overview_delete.cpp
3536
gdalalg_raster_read.cpp

apps/gdal_contour_lib.cpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ struct GDALContourOptions
4949
std::vector<std::string> aosFixedLevels{};
5050
CPLStringList aosOpenOptions{};
5151
CPLStringList aosCreationOptions{};
52+
CPLStringList aosLayerCreationOptions{};
5253
bool bQuiet = false;
5354
std::string osDestDataSource{};
5455
std::string osSrcDataSource{};
@@ -159,7 +160,7 @@ CPLErr GDALContourProcessOptions(GDALContourOptions *psOptions,
159160
psOptions->bPolygonize
160161
? (psOptions->b3D ? wkbMultiPolygon25D : wkbMultiPolygon)
161162
: (psOptions->b3D ? wkbLineString25D : wkbLineString),
162-
psOptions->aosCreationOptions);
163+
psOptions->aosLayerCreationOptions);
163164
};
164165

165166
/* -------------------------------------------------------------------- */
@@ -209,9 +210,9 @@ CPLErr GDALContourProcessOptions(GDALContourOptions *psOptions,
209210

210211
if (!*hDstDS)
211212
{
212-
*hDstDS = OGR_Dr_CreateDataSource(
213-
hDriver, psOptions->osDestDataSource.c_str(),
214-
psOptions->aosCreationOptions);
213+
*hDstDS =
214+
GDALCreate(hDriver, psOptions->osDestDataSource.c_str(), 0, 0,
215+
0, GDT_Unknown, psOptions->aosCreationOptions);
215216
}
216217

217218
if (*hDstDS == nullptr)
@@ -570,8 +571,10 @@ GDALContourAppOptionsGetParser(GDALContourOptions *psOptions,
570571

571572
argParser->add_output_format_argument(psOptions->osFormat);
572573

574+
argParser->add_creation_options_argument(psOptions->aosCreationOptions);
575+
573576
argParser->add_layer_creation_options_argument(
574-
psOptions->aosCreationOptions);
577+
psOptions->aosLayerCreationOptions);
575578
}
576579

577580
return argParser;

apps/gdalalg_raster.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "gdalalg_raster_mosaic.h"
3030
#include "gdalalg_raster_overview.h"
3131
#include "gdalalg_raster_pipeline.h"
32+
#include "gdalalg_raster_polygonize.h"
3233
#include "gdalalg_raster_reproject.h"
3334
#include "gdalalg_raster_resize.h"
3435
#include "gdalalg_raster_roughness.h"
@@ -70,6 +71,7 @@ class GDALRasterAlgorithm final : public GDALAlgorithm
7071
RegisterSubAlgorithm<GDALRasterPipelineAlgorithm>();
7172
RegisterSubAlgorithm<GDALRasterReprojectAlgorithmStandalone>();
7273
RegisterSubAlgorithm<GDALRasterMosaicAlgorithm>();
74+
RegisterSubAlgorithm<GDALRasterPolygonizeAlgorithm>();
7375
RegisterSubAlgorithm<GDALRasterResizeAlgorithmStandalone>();
7476
RegisterSubAlgorithm<GDALRasterRoughnessAlgorithmStandalone>();
7577
RegisterSubAlgorithm<GDALRasterContourAlgorithm>();

apps/gdalalg_raster_contour.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ GDALRasterContourAlgorithm::GDALRasterContourAlgorithm()
3838
AddProgressArg();
3939
AddOutputFormatArg(&m_outputFormat)
4040
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
41-
{GDAL_DCAP_VECTOR, GDAL_DCAP_CREATECOPY});
41+
{GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
4242
AddOpenOptionsArg(&m_openOptions);
4343
AddInputFormatsArg(&m_inputFormats)
4444
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
4545
AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
46-
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
46+
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR);
4747
AddCreationOptionsArg(&m_creationOptions);
48+
AddLayerCreationOptionsArg(&m_layerCreationOptions);
4849

4950
// gdal_contour specific options
5051
AddBandArg(&m_band).SetDefault(1);
@@ -107,6 +108,13 @@ bool GDALRasterContourAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
107108
aosOptions.AddString("-co");
108109
aosOptions.AddString(co);
109110
}
111+
112+
for (const auto &co : m_layerCreationOptions)
113+
{
114+
aosOptions.AddString("-lco");
115+
aosOptions.AddString(co);
116+
}
117+
110118
if (m_band > 0)
111119
{
112120
aosOptions.AddString("-b");

apps/gdalalg_raster_contour.h

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class GDALRasterContourAlgorithm final : public GDALAlgorithm
5555
std::vector<std::string> m_inputFormats{};
5656
GDALArgDatasetValue m_outputDataset{};
5757
std::vector<std::string> m_creationOptions{};
58+
std::vector<std::string> m_layerCreationOptions{};
5859

5960
// gdal_contour specific arguments
6061
int m_band = 1; // -b

apps/gdalalg_raster_polygonize.cpp

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

0 commit comments

Comments
 (0)