Line data Source code
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 : #define _(x) (x) 24 : #endif 25 : 26 91 : GDALVectorConcaveHullAlgorithm::GDALVectorConcaveHullAlgorithm( 27 91 : bool standaloneStep) 28 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL, 29 91 : standaloneStep, m_opts) 30 : { 31 182 : AddArg("ratio", 0, _("Ratio controlling the concavity"), &m_opts.m_ratio) 32 91 : .SetRequired() 33 91 : .SetMinValueIncluded(0) 34 91 : .SetMaxValueIncluded(1); 35 : 36 : AddArg("allow-holes", 0, _("Allow holes in the output polygon"), 37 182 : &m_opts.m_allowHoles) 38 91 : .SetDefault(false); 39 : 40 : AddArg("tight", 0, 41 : _("Whether the hull must follow the outer boundaries of the input " 42 : "polygons"), 43 182 : &m_opts.m_tight) 44 91 : .SetDefault(false); 45 91 : } 46 : 47 : #ifdef HAVE_GEOS 48 : 49 : namespace 50 : { 51 : 52 : class GDALVectorConcaveHullAlgorithmLayer final 53 : : public GDALVectorGeomOneToOneAlgorithmLayer< 54 : GDALVectorConcaveHullAlgorithm> 55 : { 56 : public: 57 12 : GDALVectorConcaveHullAlgorithmLayer( 58 : OGRLayer &oSrcLayer, 59 : const GDALVectorConcaveHullAlgorithm::Options &opts) 60 12 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorConcaveHullAlgorithm>( 61 : oSrcLayer, opts), 62 12 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone()) 63 : { 64 12 : 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 24 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i) 69 : { 70 12 : if (IsSelectedGeomField(i)) 71 : { 72 : const auto eType = 73 12 : m_poFeatureDefn->GetGeomFieldDefn(i)->GetType(); 74 12 : if (eType != wkbPolygon && eType != wkbMultiPolygon) 75 8 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbUnknown); 76 : } 77 : } 78 12 : } 79 : 80 24 : ~GDALVectorConcaveHullAlgorithmLayer() override 81 12 : { 82 12 : m_poFeatureDefn->Release(); 83 24 : } 84 : 85 14 : const OGRFeatureDefn *GetLayerDefn() const override 86 : { 87 14 : return m_poFeatureDefn; 88 : } 89 : 90 : protected: 91 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature; 92 : 93 : std::unique_ptr<OGRFeature> 94 12 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override 95 : { 96 12 : const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount(); 97 24 : for (int i = 0; i < nGeomFieldCount; ++i) 98 : { 99 12 : if (!IsSelectedGeomField(i)) 100 0 : continue; 101 : 102 12 : if (const OGRGeometry *poGeom = poSrcFeature->GetGeomFieldRef(i)) 103 : { 104 12 : const auto eType = wkbFlatten(poGeom->getGeometryType()); 105 : std::unique_ptr<OGRGeometry> poHull( 106 10 : (eType == wkbPolygon || eType == wkbMultiPolygon) 107 6 : ? poGeom->ConcaveHullOfPolygons(m_opts.m_ratio, 108 6 : m_opts.m_tight, 109 6 : m_opts.m_allowHoles) 110 6 : : poGeom->ConcaveHull(m_opts.m_ratio, 111 30 : m_opts.m_allowHoles)); 112 12 : if (!poHull) 113 : { 114 0 : CPLError( 115 : CE_Failure, CPLE_AppDefined, 116 : "Failed to compute concave hull of feature %" PRId64, 117 0 : static_cast<int64_t>(poSrcFeature->GetFID())); 118 0 : return nullptr; 119 : } 120 : const auto eLayerGeomType = 121 12 : m_poFeatureDefn->GetGeomFieldDefn(i)->GetType(); 122 12 : if (eLayerGeomType == wkbPolygon) 123 : { 124 0 : poHull.reset( 125 : OGRGeometryFactory::forceToPolygon(poHull.release())); 126 : } 127 12 : else if (eLayerGeomType == wkbMultiPolygon) 128 : { 129 4 : poHull.reset(OGRGeometryFactory::forceToMultiPolygon( 130 : poHull.release())); 131 : } 132 : 133 12 : poHull->assignSpatialReference(poGeom->getSpatialReference()); 134 12 : poSrcFeature->SetGeomField(i, std::move(poHull)); 135 : } 136 : } 137 : 138 12 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn); 139 12 : 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 12 : GDALVectorConcaveHullAlgorithm::CreateAlgLayer( 154 : [[maybe_unused]] OGRLayer &srcLayer) 155 : { 156 : #ifdef HAVE_GEOS 157 12 : return std::make_unique<GDALVectorConcaveHullAlgorithmLayer>(srcLayer, 158 12 : m_opts); 159 : #else 160 : CPLAssert(false); 161 : return nullptr; 162 : #endif 163 : } 164 : 165 12 : bool GDALVectorConcaveHullAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) 166 : { 167 : #ifdef HAVE_GEOS 168 12 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt); 169 : #else 170 : (void)ctxt; 171 : ReportError(CE_Failure, CPLE_NotSupported, 172 : "This algorithm is only supported for builds against GEOS"); 173 : return false; 174 : #endif 175 : } 176 : 177 : GDALVectorConcaveHullAlgorithmStandalone:: 178 : ~GDALVectorConcaveHullAlgorithmStandalone() = default; 179 : 180 : //! @endcond