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_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
        // Concave hull output type can vary; advertise unknown to avoid schema conflicts.
65
        // In polygon/multipolygoon mode, preserve input type
66
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
67
        {
68
            if (IsSelectedGeomField(i))
69
            {
70
                const auto eType =
71
                    m_poFeatureDefn->GetGeomFieldDefn(i)->GetType();
72
                if (eType != wkbPolygon && eType != wkbMultiPolygon)
73
                    m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbUnknown);
74
            }
75
        }
76
    }
77
78
    const OGRFeatureDefn *GetLayerDefn() const override
79
    {
80
        return m_poFeatureDefn.get();
81
    }
82
83
  protected:
84
    using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
85
86
    std::unique_ptr<OGRFeature>
87
    TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override
88
    {
89
        const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount();
90
        for (int i = 0; i < nGeomFieldCount; ++i)
91
        {
92
            if (!IsSelectedGeomField(i))
93
                continue;
94
95
            if (const OGRGeometry *poGeom = poSrcFeature->GetGeomFieldRef(i))
96
            {
97
                const auto eType = wkbFlatten(poGeom->getGeometryType());
98
                std::unique_ptr<OGRGeometry> poHull(
99
                    (eType == wkbPolygon || eType == wkbMultiPolygon)
100
                        ? poGeom->ConcaveHullOfPolygons(m_opts.m_ratio,
101
                                                        m_opts.m_tight,
102
                                                        m_opts.m_allowHoles)
103
                        : poGeom->ConcaveHull(m_opts.m_ratio,
104
                                              m_opts.m_allowHoles));
105
                if (!poHull)
106
                {
107
                    CPLError(
108
                        CE_Failure, CPLE_AppDefined,
109
                        "Failed to compute concave hull of feature %" PRId64,
110
                        static_cast<int64_t>(poSrcFeature->GetFID()));
111
                    return nullptr;
112
                }
113
                const auto eLayerGeomType =
114
                    m_poFeatureDefn->GetGeomFieldDefn(i)->GetType();
115
                if (eLayerGeomType == wkbPolygon)
116
                {
117
                    poHull.reset(
118
                        OGRGeometryFactory::forceToPolygon(poHull.release()));
119
                }
120
                else if (eLayerGeomType == wkbMultiPolygon)
121
                {
122
                    poHull.reset(OGRGeometryFactory::forceToMultiPolygon(
123
                        poHull.release()));
124
                }
125
126
                poHull->assignSpatialReference(poGeom->getSpatialReference());
127
                poSrcFeature->SetGeomField(i, std::move(poHull));
128
            }
129
        }
130
131
        poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
132
        return poSrcFeature;
133
    }
134
135
  private:
136
    const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
137
138
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorConcaveHullAlgorithmLayer)
139
};
140
141
}  // namespace
142
143
#endif  // HAVE_GEOS
144
145
std::unique_ptr<OGRLayerWithTranslateFeature>
146
GDALVectorConcaveHullAlgorithm::CreateAlgLayer(
147
    [[maybe_unused]] OGRLayer &srcLayer)
148
0
{
149
#ifdef HAVE_GEOS
150
    return std::make_unique<GDALVectorConcaveHullAlgorithmLayer>(srcLayer,
151
                                                                 m_opts);
152
#else
153
0
    CPLAssert(false);
154
0
    return nullptr;
155
0
#endif
156
0
}
157
158
bool GDALVectorConcaveHullAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
159
0
{
160
#ifdef HAVE_GEOS
161
    return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
162
#else
163
0
    (void)ctxt;
164
0
    ReportError(CE_Failure, CPLE_NotSupported,
165
0
                "This algorithm is only supported for builds against GEOS");
166
0
    return false;
167
0
#endif
168
0
}
169
170
GDALVectorConcaveHullAlgorithmStandalone::
171
0
    ~GDALVectorConcaveHullAlgorithmStandalone() = default;
172
173
//! @endcond