Coverage Report

Created: 2026-04-10 07:04

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
  protected:
103
    using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
104
105
    std::unique_ptr<OGRFeature>
106
    TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
107
108
  private:
109
    CPLStringList m_aosBufferOptions{};
110
    const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
111
112
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorBufferAlgorithmLayer)
113
};
114
115
/************************************************************************/
116
/*                          TranslateFeature()                          */
117
/************************************************************************/
118
119
std::unique_ptr<OGRFeature> GDALVectorBufferAlgorithmLayer::TranslateFeature(
120
    std::unique_ptr<OGRFeature> poSrcFeature) const
121
{
122
    const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
123
    for (int i = 0; i < nGeomFieldCount; ++i)
124
    {
125
        if (IsSelectedGeomField(i))
126
        {
127
            if (auto poGeom = std::unique_ptr<OGRGeometry>(
128
                    poSrcFeature->StealGeometry(i)))
129
            {
130
                poGeom.reset(poGeom->BufferEx(m_opts.m_distance,
131
                                              m_aosBufferOptions.List()));
132
                if (poGeom)
133
                {
134
                    const auto poGeomFieldDefn =
135
                        m_poFeatureDefn->GetGeomFieldDefn(i);
136
                    poGeom = OGRGeometryFactory::forceTo(
137
                        std::move(poGeom), poGeomFieldDefn->GetType());
138
                    if (poGeom)
139
                    {
140
                        poGeom->assignSpatialReference(
141
                            poGeomFieldDefn->GetSpatialRef());
142
                        poSrcFeature->SetGeomField(i, std::move(poGeom));
143
                    }
144
                }
145
            }
146
        }
147
    }
148
149
    poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
150
    return poSrcFeature;
151
}
152
153
}  // namespace
154
155
#endif  // HAVE_GEOS
156
157
/************************************************************************/
158
/*             GDALVectorBufferAlgorithm::CreateAlgLayer()              */
159
/************************************************************************/
160
161
std::unique_ptr<OGRLayerWithTranslateFeature>
162
GDALVectorBufferAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer)
163
0
{
164
#ifdef HAVE_GEOS
165
    return std::make_unique<GDALVectorBufferAlgorithmLayer>(srcLayer, m_opts);
166
#else
167
0
    CPLAssert(false);
168
0
    return nullptr;
169
0
#endif
170
0
}
171
172
/************************************************************************/
173
/*                 GDALVectorBufferAlgorithm::RunStep()                 */
174
/************************************************************************/
175
176
bool GDALVectorBufferAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
177
0
{
178
#ifdef HAVE_GEOS
179
    if (m_opts.m_side == "right")
180
        m_opts.m_distance = -m_opts.m_distance;
181
182
    return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
183
#else
184
0
    (void)ctxt;
185
0
    ReportError(CE_Failure, CPLE_NotSupported,
186
0
                "This algorithm is only supported for builds against GEOS");
187
0
    return false;
188
0
#endif
189
0
}
190
191
0
GDALVectorBufferAlgorithmStandalone::~GDALVectorBufferAlgorithmStandalone() =
192
    default;
193
194
//! @endcond