Line data Source code
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 : #define _(x) (x) 22 : #endif 23 : 24 : /************************************************************************/ 25 : /* GDALVectorBufferAlgorithm() */ 26 : /************************************************************************/ 27 : 28 30 : GDALVectorBufferAlgorithm::GDALVectorBufferAlgorithm(bool standaloneStep) 29 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL, 30 30 : standaloneStep, m_opts) 31 : { 32 : AddArg("distance", 0, _("Distance to which to extend the geometry."), 33 60 : &m_opts.m_distance) 34 30 : .SetPositional() 35 30 : .SetRequired(); 36 60 : AddArg("endcap-style", 0, _("Endcap style."), &m_opts.m_endCapStyle) 37 30 : .SetChoices("round", "flat", "square") 38 30 : .SetDefault(m_opts.m_endCapStyle); 39 60 : AddArg("join-style", 0, _("Join style."), &m_opts.m_joinStyle) 40 30 : .SetChoices("round", "mitre", "bevel") 41 30 : .SetDefault(m_opts.m_joinStyle); 42 : AddArg("mitre-limit", 0, 43 : _("Mitre ratio limit (only affects mitered join style)."), 44 60 : &m_opts.m_mitreLimit) 45 30 : .SetDefault(m_opts.m_mitreLimit) 46 30 : .SetMinValueIncluded(0); 47 : AddArg("quadrant-segments", 0, 48 : _("Number of line segments used to approximate a quarter circle."), 49 60 : &m_opts.m_quadrantSegments) 50 30 : .SetDefault(m_opts.m_quadrantSegments) 51 30 : .SetMinValueIncluded(1); 52 : AddArg("side", 0, 53 : _("Sets whether the computed buffer should be single-sided or not."), 54 60 : &m_opts.m_side) 55 30 : .SetChoices("both", "left", "right") 56 30 : .SetDefault(m_opts.m_side); 57 30 : } 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 7 : GDALVectorBufferAlgorithmLayer( 73 : OGRLayer &oSrcLayer, const GDALVectorBufferAlgorithm::Options &opts) 74 7 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorBufferAlgorithm>( 75 7 : oSrcLayer, opts) 76 : { 77 : m_aosBufferOptions.SetNameValue("ENDCAP_STYLE", 78 7 : opts.m_endCapStyle.c_str()); 79 7 : m_aosBufferOptions.SetNameValue("JOIN_STYLE", opts.m_joinStyle.c_str()); 80 : m_aosBufferOptions.SetNameValue("MITRE_LIMIT", 81 7 : CPLSPrintf("%.17g", opts.m_mitreLimit)); 82 : m_aosBufferOptions.SetNameValue( 83 7 : "QUADRANT_SEGMENTS", CPLSPrintf("%d", opts.m_quadrantSegments)); 84 : m_aosBufferOptions.SetNameValue("SINGLE_SIDED", 85 7 : m_opts.m_side != "both" ? "YES" : "NO"); 86 7 : } 87 : 88 : protected: 89 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature; 90 : 91 : std::unique_ptr<OGRFeature> 92 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override; 93 : 94 : private: 95 : CPLStringList m_aosBufferOptions{}; 96 : }; 97 : 98 : /************************************************************************/ 99 : /* TranslateFeature() */ 100 : /************************************************************************/ 101 : 102 14 : std::unique_ptr<OGRFeature> GDALVectorBufferAlgorithmLayer::TranslateFeature( 103 : std::unique_ptr<OGRFeature> poSrcFeature) const 104 : { 105 14 : const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount(); 106 28 : for (int i = 0; i < nGeomFieldCount; ++i) 107 : { 108 14 : if (IsSelectedGeomField(i)) 109 : { 110 14 : if (auto poGeom = std::unique_ptr<OGRGeometry>( 111 28 : poSrcFeature->StealGeometry(i))) 112 : { 113 14 : poGeom.reset(poGeom->BufferEx(m_opts.m_distance, 114 7 : m_aosBufferOptions.List())); 115 7 : if (poGeom) 116 : { 117 14 : poGeom->assignSpatialReference(m_srcLayer.GetLayerDefn() 118 7 : ->GetGeomFieldDefn(i) 119 14 : ->GetSpatialRef()); 120 7 : poSrcFeature->SetGeomField(i, std::move(poGeom)); 121 : } 122 : } 123 : } 124 : } 125 : 126 14 : return poSrcFeature; 127 : } 128 : 129 : } // namespace 130 : 131 : #endif // HAVE_GEOS 132 : 133 : /************************************************************************/ 134 : /* GDALVectorBufferAlgorithm::CreateAlgLayer() */ 135 : /************************************************************************/ 136 : 137 : std::unique_ptr<OGRLayerWithTranslateFeature> 138 7 : GDALVectorBufferAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer) 139 : { 140 : #ifdef HAVE_GEOS 141 7 : return std::make_unique<GDALVectorBufferAlgorithmLayer>(srcLayer, m_opts); 142 : #else 143 : CPLAssert(false); 144 : return nullptr; 145 : #endif 146 : } 147 : 148 : /************************************************************************/ 149 : /* GDALVectorBufferAlgorithm::RunStep() */ 150 : /************************************************************************/ 151 : 152 7 : bool GDALVectorBufferAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) 153 : { 154 : #ifdef HAVE_GEOS 155 7 : if (m_opts.m_side == "right") 156 1 : m_opts.m_distance = -m_opts.m_distance; 157 : 158 7 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt); 159 : #else 160 : (void)ctxt; 161 : ReportError(CE_Failure, CPLE_NotSupported, 162 : "This algorithm is only supported for builds against GEOS"); 163 : return false; 164 : #endif 165 : } 166 : 167 : GDALVectorBufferAlgorithmStandalone::~GDALVectorBufferAlgorithmStandalone() = 168 : default; 169 : 170 : //! @endcond