Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_make_valid.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "gdal vector make-valid"
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_vector_make_valid.h"
14
15
#include "gdal_priv.h"
16
#include "ogrsf_frmts.h"
17
18
#ifdef HAVE_GEOS
19
#include "ogr_geos.h"
20
#endif
21
22
//! @cond Doxygen_Suppress
23
24
#ifndef _
25
0
#define _(x) (x)
26
#endif
27
28
/************************************************************************/
29
/*                    GDALVectorMakeValidAlgorithm()                    */
30
/************************************************************************/
31
32
GDALVectorMakeValidAlgorithm::GDALVectorMakeValidAlgorithm(bool standaloneStep)
33
0
    : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
34
0
                                      standaloneStep, m_opts)
35
0
{
36
0
    AddArg("method", 0,
37
0
           _("Algorithm to use when repairing invalid geometries."),
38
0
           &m_opts.m_method)
39
0
        .SetChoices("linework", "structure")
40
0
        .SetDefault(m_opts.m_method);
41
0
    AddArg("keep-lower-dim", 0,
42
0
           _("Keep components of lower dimension after MakeValid()"),
43
0
           &m_opts.m_keepLowerDim);
44
0
}
45
46
#ifdef HAVE_GEOS
47
48
namespace
49
{
50
51
/************************************************************************/
52
/*                  GDALVectorMakeValidAlgorithmLayer                   */
53
/************************************************************************/
54
55
class GDALVectorMakeValidAlgorithmLayer final
56
    : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorMakeValidAlgorithm>
57
{
58
  public:
59
    GDALVectorMakeValidAlgorithmLayer(
60
        OGRLayer &oSrcLayer, const GDALVectorMakeValidAlgorithm::Options &opts)
61
        : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorMakeValidAlgorithm>(
62
              oSrcLayer, opts)
63
    {
64
        if (m_opts.m_method == "structure")
65
        {
66
            m_aosMakeValidOptions.SetNameValue("METHOD", "STRUCTURE");
67
            m_aosMakeValidOptions.SetNameValue(
68
                "KEEP_COLLAPSED", m_opts.m_keepLowerDim ? "YES" : "NO");
69
        }
70
    }
71
72
  protected:
73
    using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
74
75
    std::unique_ptr<OGRFeature>
76
    TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
77
78
  private:
79
    CPLStringList m_aosMakeValidOptions{};
80
};
81
82
/************************************************************************/
83
/*                          TranslateFeature()                          */
84
/************************************************************************/
85
86
std::unique_ptr<OGRFeature> GDALVectorMakeValidAlgorithmLayer::TranslateFeature(
87
    std::unique_ptr<OGRFeature> poSrcFeature) const
88
{
89
    CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
90
    const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount();
91
    for (int i = 0; i < nGeomFieldCount; ++i)
92
    {
93
        if (IsSelectedGeomField(i))
94
        {
95
            auto poGeom =
96
                std::unique_ptr<OGRGeometry>(poSrcFeature->StealGeometry(i));
97
            if (poGeom && !poGeom->IsValid())
98
            {
99
                const bool bIsGeomCollection =
100
                    wkbFlatten(poGeom->getGeometryType()) ==
101
                    wkbGeometryCollection;
102
#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 11
103
                const bool bSrcIs3D = poGeom->Is3D();
104
#endif
105
                poGeom.reset(poGeom->MakeValid(m_aosMakeValidOptions.List()));
106
                if (poGeom)
107
                {
108
#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 11
109
                    if (!bSrcIs3D && poGeom->Is3D())
110
                        poGeom->flattenTo2D();
111
#endif
112
                    if (!bIsGeomCollection && !m_opts.m_keepLowerDim)
113
                    {
114
                        poGeom.reset(
115
                            OGRGeometryFactory::removeLowerDimensionSubGeoms(
116
                                poGeom.get()));
117
                    }
118
                    poGeom->assignSpatialReference(m_srcLayer.GetLayerDefn()
119
                                                       ->GetGeomFieldDefn(i)
120
                                                       ->GetSpatialRef());
121
                }
122
            }
123
            if (poGeom)
124
            {
125
                poSrcFeature->SetGeomField(i, std::move(poGeom));
126
            }
127
        }
128
    }
129
130
    return poSrcFeature;
131
}
132
133
}  // namespace
134
135
#endif  // HAVE_GEOS
136
137
/************************************************************************/
138
/*            GDALVectorMakeValidAlgorithm::CreateAlgLayer()            */
139
/************************************************************************/
140
141
std::unique_ptr<OGRLayerWithTranslateFeature>
142
GDALVectorMakeValidAlgorithm::CreateAlgLayer(
143
    [[maybe_unused]] OGRLayer &srcLayer)
144
0
{
145
#ifdef HAVE_GEOS
146
    return std::make_unique<GDALVectorMakeValidAlgorithmLayer>(srcLayer,
147
                                                               m_opts);
148
#else
149
0
    CPLAssert(false);
150
0
    return nullptr;
151
0
#endif
152
0
}
153
154
/************************************************************************/
155
/*               GDALVectorMakeValidAlgorithm::RunStep()                */
156
/************************************************************************/
157
158
bool GDALVectorMakeValidAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
159
0
{
160
#ifdef HAVE_GEOS
161
162
#if !(GEOS_VERSION_MAJOR > 3 ||                                                \
163
      (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
164
    if (m_opts.m_method == "structure")
165
    {
166
        ReportError(
167
            CE_Failure, CPLE_NotSupported,
168
            "method = 'structure' requires a build against GEOS >= 3.10");
169
        return false;
170
    }
171
#endif
172
173
    return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
174
#else
175
0
    (void)ctxt;
176
0
    ReportError(CE_Failure, CPLE_NotSupported,
177
0
                "This algorithm is only supported for builds against GEOS");
178
0
    return false;
179
0
#endif
180
0
}
181
182
GDALVectorMakeValidAlgorithmStandalone::
183
0
    ~GDALVectorMakeValidAlgorithmStandalone() = default;
184
185
//! @endcond