Coverage Report

Created: 2026-04-01 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_concave_hull.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "gdal vector concave-hull"
5
 * Author:   Daniel Baston
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2026, ISciences LLC
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_vector_concave_hull.h"
14
15
#include "gdal_priv.h"
16
#include "ogrsf_frmts.h"
17
18
#include <cinttypes>
19
20
//! @cond Doxygen_Suppress
21
22
#ifndef _
23
0
#define _(x) (x)
24
#endif
25
26
GDALVectorConcaveHullAlgorithm::GDALVectorConcaveHullAlgorithm(
27
    bool standaloneStep)
28
0
    : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
29
0
                                      standaloneStep, m_opts)
30
0
{
31
0
    AddArg("ratio", 0, _("Ratio controlling the concavity"), &m_opts.m_ratio)
32
0
        .SetRequired()
33
0
        .SetMinValueIncluded(0)
34
0
        .SetMaxValueIncluded(1);
35
36
0
    AddArg("allow-holes", 0, _("Allow holes in the output polygon"),
37
0
           &m_opts.m_allowHoles)
38
0
        .SetDefault(false);
39
40
0
    AddArg("tight", 0,
41
0
           _("Whether the hull must follow the outer boundaries of the input "
42
0
             "polygons"),
43
0
           &m_opts.m_tight)
44
0
        .SetDefault(false);
45
0
}
46
47
#ifdef HAVE_GEOS
48
49
namespace
50
{
51
52
class GDALVectorConcaveHullAlgorithmLayer final
53
    : public GDALVectorGeomOneToOneAlgorithmLayer<
54
          GDALVectorConcaveHullAlgorithm>
55
{
56
  public:
57
    GDALVectorConcaveHullAlgorithmLayer(
58
        OGRLayer &oSrcLayer,
59
        const GDALVectorConcaveHullAlgorithm::Options &opts)
60
        : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorConcaveHullAlgorithm>(
61
              oSrcLayer, opts),
62
          m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
63
    {
64
        m_poFeatureDefn->Reference();
65
66
        // Concave hull output type can vary; advertise unknown to avoid schema conflicts.
67
        // In polygon/multipolygoon mode, preserve input type
68
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
69
        {
70
            if (IsSelectedGeomField(i))
71
            {
72
                const auto eType =
73
                    m_poFeatureDefn->GetGeomFieldDefn(i)->GetType();
74
                if (eType != wkbPolygon && eType != wkbMultiPolygon)
75
                    m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbUnknown);
76
            }
77
        }
78
    }
79
80
    ~GDALVectorConcaveHullAlgorithmLayer() override
81
    {
82
        m_poFeatureDefn->Release();
83
    }
84
85
    const OGRFeatureDefn *GetLayerDefn() const override
86
    {
87
        return m_poFeatureDefn;
88
    }
89
90
  protected:
91
    using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
92
93
    std::unique_ptr<OGRFeature>
94
    TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override
95
    {
96
        const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount();
97
        for (int i = 0; i < nGeomFieldCount; ++i)
98
        {
99
            if (!IsSelectedGeomField(i))
100
                continue;
101
102
            if (const OGRGeometry *poGeom = poSrcFeature->GetGeomFieldRef(i))
103
            {
104
                const auto eType = wkbFlatten(poGeom->getGeometryType());
105
                std::unique_ptr<OGRGeometry> poHull(
106
                    (eType == wkbPolygon || eType == wkbMultiPolygon)
107
                        ? poGeom->ConcaveHullOfPolygons(m_opts.m_ratio,
108
                                                        m_opts.m_tight,
109
                                                        m_opts.m_allowHoles)
110
                        : poGeom->ConcaveHull(m_opts.m_ratio,
111
                                              m_opts.m_allowHoles));
112
                if (!poHull)
113
                {
114
                    CPLError(
115
                        CE_Failure, CPLE_AppDefined,
116
                        "Failed to compute concave hull of feature %" PRId64,
117
                        static_cast<int64_t>(poSrcFeature->GetFID()));
118
                    return nullptr;
119
                }
120
                const auto eLayerGeomType =
121
                    m_poFeatureDefn->GetGeomFieldDefn(i)->GetType();
122
                if (eLayerGeomType == wkbPolygon)
123
                {
124
                    poHull.reset(
125
                        OGRGeometryFactory::forceToPolygon(poHull.release()));
126
                }
127
                else if (eLayerGeomType == wkbMultiPolygon)
128
                {
129
                    poHull.reset(OGRGeometryFactory::forceToMultiPolygon(
130
                        poHull.release()));
131
                }
132
133
                poHull->assignSpatialReference(poGeom->getSpatialReference());
134
                poSrcFeature->SetGeomField(i, std::move(poHull));
135
            }
136
        }
137
138
        poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn);
139
        return poSrcFeature;
140
    }
141
142
  private:
143
    OGRFeatureDefn *const m_poFeatureDefn;
144
145
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorConcaveHullAlgorithmLayer)
146
};
147
148
}  // namespace
149
150
#endif  // HAVE_GEOS
151
152
std::unique_ptr<OGRLayerWithTranslateFeature>
153
GDALVectorConcaveHullAlgorithm::CreateAlgLayer(
154
    [[maybe_unused]] OGRLayer &srcLayer)
155
0
{
156
#ifdef HAVE_GEOS
157
    return std::make_unique<GDALVectorConcaveHullAlgorithmLayer>(srcLayer,
158
                                                                 m_opts);
159
#else
160
0
    CPLAssert(false);
161
0
    return nullptr;
162
0
#endif
163
0
}
164
165
bool GDALVectorConcaveHullAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
166
0
{
167
#ifdef HAVE_GEOS
168
    return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
169
#else
170
0
    (void)ctxt;
171
0
    ReportError(CE_Failure, CPLE_NotSupported,
172
0
                "This algorithm is only supported for builds against GEOS");
173
0
    return false;
174
0
#endif
175
0
}
176
177
GDALVectorConcaveHullAlgorithmStandalone::
178
0
    ~GDALVectorConcaveHullAlgorithmStandalone() = default;
179
180
//! @endcond