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_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
        m_poFeatureDefn->Reference();
79
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
80
        {
81
            if (IsSelectedGeomField(i))
82
            {
83
                m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbMultiPolygon);
84
            }
85
        }
86
87
        m_aosBufferOptions.SetNameValue("ENDCAP_STYLE",
88
                                        opts.m_endCapStyle.c_str());
89
        m_aosBufferOptions.SetNameValue("JOIN_STYLE", opts.m_joinStyle.c_str());
90
        m_aosBufferOptions.SetNameValue("MITRE_LIMIT",
91
                                        CPLSPrintf("%.17g", opts.m_mitreLimit));
92
        m_aosBufferOptions.SetNameValue(
93
            "QUADRANT_SEGMENTS", CPLSPrintf("%d", opts.m_quadrantSegments));
94
        m_aosBufferOptions.SetNameValue("SINGLE_SIDED",
95
                                        m_opts.m_side != "both" ? "YES" : "NO");
96
    }
97
98
    ~GDALVectorBufferAlgorithmLayer() override
99
    {
100
        m_poFeatureDefn->Release();
101
    }
102
103
    const OGRFeatureDefn *GetLayerDefn() const override
104
    {
105
        return m_poFeatureDefn;
106
    }
107
108
  protected:
109
    using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
110
111
    std::unique_ptr<OGRFeature>
112
    TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
113
114
  private:
115
    CPLStringList m_aosBufferOptions{};
116
    OGRFeatureDefn *const m_poFeatureDefn;
117
118
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorBufferAlgorithmLayer)
119
};
120
121
/************************************************************************/
122
/*                          TranslateFeature()                          */
123
/************************************************************************/
124
125
std::unique_ptr<OGRFeature> GDALVectorBufferAlgorithmLayer::TranslateFeature(
126
    std::unique_ptr<OGRFeature> poSrcFeature) const
127
{
128
    const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
129
    for (int i = 0; i < nGeomFieldCount; ++i)
130
    {
131
        if (IsSelectedGeomField(i))
132
        {
133
            if (auto poGeom = std::unique_ptr<OGRGeometry>(
134
                    poSrcFeature->StealGeometry(i)))
135
            {
136
                poGeom.reset(poGeom->BufferEx(m_opts.m_distance,
137
                                              m_aosBufferOptions.List()));
138
                if (poGeom)
139
                {
140
                    const auto poGeomFieldDefn =
141
                        m_poFeatureDefn->GetGeomFieldDefn(i);
142
                    poGeom = OGRGeometryFactory::forceTo(
143
                        std::move(poGeom), poGeomFieldDefn->GetType());
144
                    if (poGeom)
145
                    {
146
                        poGeom->assignSpatialReference(
147
                            poGeomFieldDefn->GetSpatialRef());
148
                        poSrcFeature->SetGeomField(i, std::move(poGeom));
149
                    }
150
                }
151
            }
152
        }
153
    }
154
155
    poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn);
156
    return poSrcFeature;
157
}
158
159
}  // namespace
160
161
#endif  // HAVE_GEOS
162
163
/************************************************************************/
164
/*             GDALVectorBufferAlgorithm::CreateAlgLayer()              */
165
/************************************************************************/
166
167
std::unique_ptr<OGRLayerWithTranslateFeature>
168
GDALVectorBufferAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer)
169
0
{
170
#ifdef HAVE_GEOS
171
    return std::make_unique<GDALVectorBufferAlgorithmLayer>(srcLayer, m_opts);
172
#else
173
0
    CPLAssert(false);
174
0
    return nullptr;
175
0
#endif
176
0
}
177
178
/************************************************************************/
179
/*                 GDALVectorBufferAlgorithm::RunStep()                 */
180
/************************************************************************/
181
182
bool GDALVectorBufferAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
183
0
{
184
#ifdef HAVE_GEOS
185
    if (m_opts.m_side == "right")
186
        m_opts.m_distance = -m_opts.m_distance;
187
188
    return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
189
#else
190
0
    (void)ctxt;
191
0
    ReportError(CE_Failure, CPLE_NotSupported,
192
0
                "This algorithm is only supported for builds against GEOS");
193
0
    return false;
194
0
#endif
195
0
}
196
197
0
GDALVectorBufferAlgorithmStandalone::~GDALVectorBufferAlgorithmStandalone() =
198
    default;
199
200
//! @endcond