Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_buffer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "gdal vector buffer"
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_buffer.h"
14
15
#include "gdal_priv.h"
16
#include "ogrsf_frmts.h"
17
18
//! @cond Doxygen_Suppress
19
20
#ifndef _
21
0
#define _(x) (x)
22
#endif
23
24
/************************************************************************/
25
/*                     GDALVectorBufferAlgorithm()                      */
26
/************************************************************************/
27
28
GDALVectorBufferAlgorithm::GDALVectorBufferAlgorithm(bool standaloneStep)
29
0
    : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
30
0
                                      standaloneStep, m_opts)
31
0
{
32
0
    AddArg("distance", 0, _("Distance to which to extend the geometry."),
33
0
           &m_opts.m_distance)
34
0
        .SetPositional()
35
0
        .SetRequired();
36
0
    AddArg("endcap-style", 0, _("Endcap style."), &m_opts.m_endCapStyle)
37
0
        .SetChoices("round", "flat", "square")
38
0
        .SetDefault(m_opts.m_endCapStyle);
39
0
    AddArg("join-style", 0, _("Join style."), &m_opts.m_joinStyle)
40
0
        .SetChoices("round", "mitre", "bevel")
41
0
        .SetDefault(m_opts.m_joinStyle);
42
0
    AddArg("mitre-limit", 0,
43
0
           _("Mitre ratio limit (only affects mitered join style)."),
44
0
           &m_opts.m_mitreLimit)
45
0
        .SetDefault(m_opts.m_mitreLimit)
46
0
        .SetMinValueIncluded(0);
47
0
    AddArg("quadrant-segments", 0,
48
0
           _("Number of line segments used to approximate a quarter circle."),
49
0
           &m_opts.m_quadrantSegments)
50
0
        .SetDefault(m_opts.m_quadrantSegments)
51
0
        .SetMinValueIncluded(1);
52
0
    AddArg("side", 0,
53
0
           _("Sets whether the computed buffer should be single-sided or not."),
54
0
           &m_opts.m_side)
55
0
        .SetChoices("both", "left", "right")
56
0
        .SetDefault(m_opts.m_side);
57
0
}
58
59
#ifdef HAVE_GEOS
60
61
namespace
62
{
63
64
/************************************************************************/
65
/*                    GDALVectorBufferAlgorithmLayer                    */
66
/************************************************************************/
67
68
class GDALVectorBufferAlgorithmLayer final
69
    : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorBufferAlgorithm>
70
{
71
  public:
72
    GDALVectorBufferAlgorithmLayer(
73
        OGRLayer &oSrcLayer, const GDALVectorBufferAlgorithm::Options &opts)
74
        : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorBufferAlgorithm>(
75
              oSrcLayer, opts),
76
          m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
77
    {
78
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
79
        {
80
            if (IsSelectedGeomField(i))
81
            {
82
                m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbMultiPolygon);
83
            }
84
        }
85
86
        m_aosBufferOptions.SetNameValue("ENDCAP_STYLE",
87
                                        opts.m_endCapStyle.c_str());
88
        m_aosBufferOptions.SetNameValue("JOIN_STYLE", opts.m_joinStyle.c_str());
89
        m_aosBufferOptions.SetNameValue("MITRE_LIMIT",
90
                                        CPLSPrintf("%.17g", opts.m_mitreLimit));
91
        m_aosBufferOptions.SetNameValue(
92
            "QUADRANT_SEGMENTS", CPLSPrintf("%d", opts.m_quadrantSegments));
93
        m_aosBufferOptions.SetNameValue("SINGLE_SIDED",
94
                                        m_opts.m_side != "both" ? "YES" : "NO");
95
    }
96
97
    const OGRFeatureDefn *GetLayerDefn() const override
98
    {
99
        return m_poFeatureDefn.get();
100
    }
101
102
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
103
                      bool bForce) override
104
    {
105
        if (IsSelectedGeomField(iGeomField))
106
            return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
107
        return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
108
    }
109
110
    bool TestCapability(const char *pszCap) const override
111
    {
112
        if (EQUAL(pszCap, OLCFastGetExtent))
113
            return false;
114
        return GDALVectorGeomOneToOneAlgorithmLayer::TestCapability(pszCap);
115
    }
116
117
  protected:
118
    using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
119
120
    std::unique_ptr<OGRFeature>
121
    TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
122
123
  private:
124
    CPLStringList m_aosBufferOptions{};
125
    const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
126
127
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorBufferAlgorithmLayer)
128
};
129
130
/************************************************************************/
131
/*                          TranslateFeature()                          */
132
/************************************************************************/
133
134
std::unique_ptr<OGRFeature> GDALVectorBufferAlgorithmLayer::TranslateFeature(
135
    std::unique_ptr<OGRFeature> poSrcFeature) const
136
{
137
    const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
138
    for (int i = 0; i < nGeomFieldCount; ++i)
139
    {
140
        if (IsSelectedGeomField(i))
141
        {
142
            if (auto poGeom = std::unique_ptr<OGRGeometry>(
143
                    poSrcFeature->StealGeometry(i)))
144
            {
145
                poGeom.reset(poGeom->BufferEx(m_opts.m_distance,
146
                                              m_aosBufferOptions.List()));
147
                if (poGeom)
148
                {
149
                    const auto poGeomFieldDefn =
150
                        m_poFeatureDefn->GetGeomFieldDefn(i);
151
                    poGeom = OGRGeometryFactory::forceTo(
152
                        std::move(poGeom), poGeomFieldDefn->GetType());
153
                    if (poGeom)
154
                    {
155
                        poGeom->assignSpatialReference(
156
                            poGeomFieldDefn->GetSpatialRef());
157
                        poSrcFeature->SetGeomField(i, std::move(poGeom));
158
                    }
159
                }
160
            }
161
        }
162
    }
163
164
    poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
165
    return poSrcFeature;
166
}
167
168
}  // namespace
169
170
#endif  // HAVE_GEOS
171
172
/************************************************************************/
173
/*             GDALVectorBufferAlgorithm::CreateAlgLayer()              */
174
/************************************************************************/
175
176
std::unique_ptr<OGRLayerWithTranslateFeature>
177
GDALVectorBufferAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer)
178
0
{
179
#ifdef HAVE_GEOS
180
    return std::make_unique<GDALVectorBufferAlgorithmLayer>(srcLayer, m_opts);
181
#else
182
0
    CPLAssert(false);
183
0
    return nullptr;
184
0
#endif
185
0
}
186
187
/************************************************************************/
188
/*                 GDALVectorBufferAlgorithm::RunStep()                 */
189
/************************************************************************/
190
191
bool GDALVectorBufferAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
192
0
{
193
#ifdef HAVE_GEOS
194
    if (m_opts.m_side == "right")
195
        m_opts.m_distance = -m_opts.m_distance;
196
197
    return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
198
#else
199
0
    (void)ctxt;
200
0
    ReportError(CE_Failure, CPLE_NotSupported,
201
0
                "This algorithm is only supported for builds against GEOS");
202
0
    return false;
203
0
#endif
204
0
}
205
206
0
GDALVectorBufferAlgorithmStandalone::~GDALVectorBufferAlgorithmStandalone() =
207
    default;
208
209
//! @endcond