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