Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL 4 : * Purpose: "gdal vector make-valid" 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_make_valid.h" 14 : 15 : #include "gdal_priv.h" 16 : #include "ogrsf_frmts.h" 17 : 18 : #ifdef HAVE_GEOS 19 : #include "ogr_geos.h" 20 : #endif 21 : 22 : //! @cond Doxygen_Suppress 23 : 24 : #ifndef _ 25 : #define _(x) (x) 26 : #endif 27 : 28 : /************************************************************************/ 29 : /* GDALVectorMakeValidAlgorithm() */ 30 : /************************************************************************/ 31 : 32 55 : GDALVectorMakeValidAlgorithm::GDALVectorMakeValidAlgorithm(bool standaloneStep) 33 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL, 34 55 : standaloneStep, m_opts) 35 : { 36 : AddArg("method", 0, 37 : _("Algorithm to use when repairing invalid geometries."), 38 110 : &m_opts.m_method) 39 55 : .SetChoices("linework", "structure") 40 55 : .SetDefault(m_opts.m_method); 41 : AddArg("keep-lower-dim", 0, 42 : _("Keep components of lower dimension after MakeValid()"), 43 55 : &m_opts.m_keepLowerDim); 44 55 : } 45 : 46 : #ifdef HAVE_GEOS 47 : 48 : namespace 49 : { 50 : 51 : /************************************************************************/ 52 : /* GDALVectorMakeValidAlgorithmLayer */ 53 : /************************************************************************/ 54 : 55 : class GDALVectorMakeValidAlgorithmLayer final 56 : : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorMakeValidAlgorithm> 57 : { 58 : public: 59 11 : GDALVectorMakeValidAlgorithmLayer( 60 : OGRLayer &oSrcLayer, const GDALVectorMakeValidAlgorithm::Options &opts) 61 11 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorMakeValidAlgorithm>( 62 11 : oSrcLayer, opts) 63 : { 64 11 : if (m_opts.m_method == "structure") 65 : { 66 2 : m_aosMakeValidOptions.SetNameValue("METHOD", "STRUCTURE"); 67 : m_aosMakeValidOptions.SetNameValue( 68 2 : "KEEP_COLLAPSED", m_opts.m_keepLowerDim ? "YES" : "NO"); 69 : } 70 11 : } 71 : 72 : protected: 73 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature; 74 : 75 : std::unique_ptr<OGRFeature> 76 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override; 77 : 78 : private: 79 : CPLStringList m_aosMakeValidOptions{}; 80 : }; 81 : 82 : /************************************************************************/ 83 : /* TranslateFeature() */ 84 : /************************************************************************/ 85 : 86 309 : std::unique_ptr<OGRFeature> GDALVectorMakeValidAlgorithmLayer::TranslateFeature( 87 : std::unique_ptr<OGRFeature> poSrcFeature) const 88 : { 89 618 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); 90 309 : const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount(); 91 619 : for (int i = 0; i < nGeomFieldCount; ++i) 92 : { 93 310 : if (IsSelectedGeomField(i)) 94 : { 95 : auto poGeom = 96 618 : std::unique_ptr<OGRGeometry>(poSrcFeature->StealGeometry(i)); 97 309 : if (poGeom && !poGeom->IsValid()) 98 : { 99 : const bool bIsGeomCollection = 100 11 : wkbFlatten(poGeom->getGeometryType()) == 101 11 : wkbGeometryCollection; 102 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 11 103 : const bool bSrcIs3D = poGeom->Is3D(); 104 : #endif 105 11 : poGeom.reset(poGeom->MakeValid(m_aosMakeValidOptions.List())); 106 11 : if (poGeom) 107 : { 108 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 11 109 : if (!bSrcIs3D && poGeom->Is3D()) 110 : poGeom->flattenTo2D(); 111 : #endif 112 11 : if (!bIsGeomCollection && !m_opts.m_keepLowerDim) 113 : { 114 9 : poGeom.reset( 115 : OGRGeometryFactory::removeLowerDimensionSubGeoms( 116 9 : poGeom.get())); 117 : } 118 22 : poGeom->assignSpatialReference(m_srcLayer.GetLayerDefn() 119 11 : ->GetGeomFieldDefn(i) 120 22 : ->GetSpatialRef()); 121 : } 122 : } 123 309 : if (poGeom) 124 : { 125 307 : poSrcFeature->SetGeomField(i, std::move(poGeom)); 126 : } 127 : } 128 : } 129 : 130 618 : return poSrcFeature; 131 : } 132 : 133 : } // namespace 134 : 135 : #endif // HAVE_GEOS 136 : 137 : /************************************************************************/ 138 : /* GDALVectorMakeValidAlgorithm::CreateAlgLayer() */ 139 : /************************************************************************/ 140 : 141 : std::unique_ptr<OGRLayerWithTranslateFeature> 142 11 : GDALVectorMakeValidAlgorithm::CreateAlgLayer( 143 : [[maybe_unused]] OGRLayer &srcLayer) 144 : { 145 : #ifdef HAVE_GEOS 146 11 : return std::make_unique<GDALVectorMakeValidAlgorithmLayer>(srcLayer, 147 11 : m_opts); 148 : #else 149 : CPLAssert(false); 150 : return nullptr; 151 : #endif 152 : } 153 : 154 : /************************************************************************/ 155 : /* GDALVectorMakeValidAlgorithm::RunStep() */ 156 : /************************************************************************/ 157 : 158 11 : bool GDALVectorMakeValidAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) 159 : { 160 : #ifdef HAVE_GEOS 161 : 162 : #if !(GEOS_VERSION_MAJOR > 3 || \ 163 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)) 164 : if (m_opts.m_method == "structure") 165 : { 166 : ReportError( 167 : CE_Failure, CPLE_NotSupported, 168 : "method = 'structure' requires a build against GEOS >= 3.10"); 169 : return false; 170 : } 171 : #endif 172 : 173 11 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt); 174 : #else 175 : (void)ctxt; 176 : ReportError(CE_Failure, CPLE_NotSupported, 177 : "This algorithm is only supported for builds against GEOS"); 178 : return false; 179 : #endif 180 : } 181 : 182 : GDALVectorMakeValidAlgorithmStandalone:: 183 : ~GDALVectorMakeValidAlgorithmStandalone() = default; 184 : 185 : //! @endcond