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 94 : GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm(
32 94 : bool standaloneStep)
33 : : GDALPipelineStepAlgorithm(
34 : NAME, DESCRIPTION, HELP_URL,
35 0 : ConstructorOptions()
36 94 : .SetStandaloneStep(standaloneStep)
37 94 : .SetAddUpsertArgument(false)
38 94 : .SetAddSkipErrorsArgument(false)
39 94 : .SetOutputLayerNameAvailableInPipelineStep(true)
40 188 : .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
41 : {
42 94 : if (standaloneStep)
43 : {
44 68 : AddProgressArg();
45 68 : AddRasterInputArgs(false, false);
46 68 : AddVectorOutputArgs(false, false);
47 : }
48 : else
49 : {
50 26 : AddRasterHiddenInputDatasetArg();
51 26 : AddOutputLayerNameArg(/* hiddenForCLI = */ false,
52 : /* shortNameOutputLayerAllowed = */ false);
53 : }
54 :
55 : // gdal_polygonize specific options
56 94 : AddBandArg(&m_band).SetDefault(m_band);
57 : AddArg("attribute-name", 0, _("Name of the field with the pixel value"),
58 188 : &m_attributeName)
59 94 : .SetDefault(m_attributeName);
60 :
61 : AddArg("connect-diagonal-pixels", 'c',
62 188 : _("Consider diagonal pixels as connected"), &m_connectDiagonalPixels)
63 94 : .SetDefault(m_connectDiagonalPixels);
64 :
65 188 : AddArg("commit-interval", 0, _("Commit interval"), &m_commitInterval)
66 94 : .SetHidden();
67 94 : }
68 :
69 4 : bool GDALRasterPolygonizeAlgorithm::CanHandleNextStep(
70 : GDALPipelineStepAlgorithm *poNextStep) const
71 : {
72 7 : return poNextStep->GetName() == GDALVectorWriteAlgorithm::NAME &&
73 7 : poNextStep->GetOutputFormat() != "stream";
74 : }
75 :
76 : /************************************************************************/
77 : /* GDALRasterPolygonizeAlgorithm::RunImpl() */
78 : /************************************************************************/
79 :
80 22 : bool GDALRasterPolygonizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
81 : void *pProgressData)
82 : {
83 22 : GDALPipelineStepRunContext stepCtxt;
84 22 : stepCtxt.m_pfnProgress = pfnProgress;
85 22 : stepCtxt.m_pProgressData = pProgressData;
86 22 : return RunPreStepPipelineValidations() && RunStep(stepCtxt);
87 : }
88 :
89 : /************************************************************************/
90 : /* GDALRasterPolygonizeAlgorithm::RunStep() */
91 : /************************************************************************/
92 :
93 28 : bool GDALRasterPolygonizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
94 : {
95 28 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
96 28 : CPLAssert(poSrcDS);
97 :
98 28 : auto poWriteStep = ctxt.m_poNextUsableStep ? ctxt.m_poNextUsableStep : this;
99 :
100 28 : GDALDataset *poDstDS = nullptr;
101 28 : bool bTemporaryFile = false;
102 28 : std::unique_ptr<GDALDataset> poNewRetDS;
103 56 : std::string outputLayerName;
104 28 : OGRLayer *poDstLayer = nullptr;
105 28 : if (!CreateDatasetSingleOutputLayerIfNeeded(ctxt, "polygonize", poDstDS,
106 : bTemporaryFile, poNewRetDS,
107 : outputLayerName, poDstLayer))
108 : {
109 4 : return false;
110 : }
111 :
112 24 : auto poSrcBand = poSrcDS->GetRasterBand(m_band);
113 24 : const auto eDT = poSrcBand->GetRasterDataType();
114 :
115 24 : if (!poDstLayer)
116 : {
117 22 : poDstLayer = poDstDS->CreateLayer(
118 22 : outputLayerName.c_str(), poSrcDS->GetSpatialRef(), wkbPolygon,
119 44 : CPLStringList(poWriteStep->GetLayerCreationOptions()).List());
120 22 : if (!poDstLayer)
121 : {
122 1 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot create layer '%s'",
123 : outputLayerName.c_str());
124 1 : return false;
125 : }
126 :
127 : OGRFieldDefn oFieldDefn(m_attributeName.c_str(),
128 21 : !GDALDataTypeIsInteger(eDT) ? OFTReal
129 19 : : eDT == GDT_Int64 || eDT == GDT_UInt64
130 39 : ? OFTInteger64
131 41 : : OFTInteger);
132 21 : if (poDstLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
133 : {
134 0 : ReportError(CE_Failure, CPLE_AppDefined,
135 : "Cannot create field '%s' in layer '%s'",
136 : m_attributeName.c_str(), outputLayerName.c_str());
137 0 : return false;
138 : }
139 : }
140 :
141 : const int iPixValField =
142 23 : poDstLayer->GetLayerDefn()->GetFieldIndex(m_attributeName.c_str());
143 23 : if (iPixValField < 0)
144 : {
145 1 : ReportError(CE_Failure, CPLE_AppDefined,
146 : "Cannot find field '%s' in layer '%s'",
147 : m_attributeName.c_str(), outputLayerName.c_str());
148 1 : return false;
149 : }
150 :
151 22 : CPLStringList aosPolygonizeOptions;
152 22 : if (m_connectDiagonalPixels)
153 : {
154 1 : aosPolygonizeOptions.SetNameValue("8CONNECTED", "8");
155 : }
156 22 : if (m_commitInterval)
157 : {
158 : aosPolygonizeOptions.SetNameValue("COMMIT_INTERVAL",
159 2 : CPLSPrintf("%d", m_commitInterval));
160 : }
161 :
162 : bool ret;
163 22 : if (GDALDataTypeIsInteger(eDT))
164 : {
165 42 : ret = GDALPolygonize(GDALRasterBand::ToHandle(poSrcBand),
166 21 : GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
167 : OGRLayer::ToHandle(poDstLayer), iPixValField,
168 : aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
169 : ctxt.m_pProgressData) == CE_None;
170 : }
171 : else
172 : {
173 1 : ret =
174 2 : GDALFPolygonize(GDALRasterBand::ToHandle(poSrcBand),
175 1 : GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
176 : OGRLayer::ToHandle(poDstLayer), iPixValField,
177 : aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
178 : ctxt.m_pProgressData) == CE_None;
179 : }
180 :
181 22 : if (ret && poNewRetDS)
182 : {
183 20 : if (bTemporaryFile)
184 : {
185 3 : ret = poNewRetDS->FlushCache() == CE_None;
186 : #if !defined(__APPLE__)
187 : // For some unknown reason, unlinking the file on MacOSX
188 : // leads to later "disk I/O error". See https://github.com/OSGeo/gdal/issues/13794
189 3 : VSIUnlink(poNewRetDS->GetDescription());
190 : #endif
191 : }
192 :
193 20 : m_outputDataset.Set(std::move(poNewRetDS));
194 : }
195 :
196 22 : return ret;
197 : }
198 :
199 : GDALRasterPolygonizeAlgorithmStandalone::
200 : ~GDALRasterPolygonizeAlgorithmStandalone() = default;
201 :
202 : //! @endcond
|