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 "geos_c.h" 20 : #endif 21 : 22 : //! @cond Doxygen_Suppress 23 : 24 : #ifndef _ 25 : #define _(x) (x) 26 : #endif 27 : 28 : /************************************************************************/ 29 : /* GDALVectorMakeValidAlgorithm() */ 30 : /************************************************************************/ 31 : 32 30 : GDALVectorMakeValidAlgorithm::GDALVectorMakeValidAlgorithm(bool standaloneStep) 33 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL, 34 30 : standaloneStep, m_opts) 35 : { 36 : AddArg("method", 0, 37 : _("Algorithm to use when repairing invalid geometries."), 38 60 : &m_opts.m_method) 39 30 : .SetChoices("linework", "structure") 40 30 : .SetDefault(m_opts.m_method); 41 : AddArg("keep-lower-dim", 0, 42 : _("Keep components of lower dimension after MakeValid()"), 43 30 : &m_opts.m_keepLowerDim); 44 30 : } 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 10 : GDALVectorMakeValidAlgorithmLayer( 60 : OGRLayer &oSrcLayer, const GDALVectorMakeValidAlgorithm::Options &opts) 61 10 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorMakeValidAlgorithm>( 62 10 : oSrcLayer, opts) 63 : { 64 10 : 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 10 : } 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 297 : std::unique_ptr<OGRFeature> GDALVectorMakeValidAlgorithmLayer::TranslateFeature( 87 : std::unique_ptr<OGRFeature> poSrcFeature) const 88 : { 89 594 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); 90 297 : const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount(); 91 595 : for (int i = 0; i < nGeomFieldCount; ++i) 92 : { 93 298 : if (IsSelectedGeomField(i)) 94 : { 95 : auto poGeom = 96 594 : std::unique_ptr<OGRGeometry>(poSrcFeature->StealGeometry(i)); 97 592 : if (poGeom && poGeom->getCoordinateDimension() == 2 && 98 295 : !poGeom->IsValid()) 99 : { 100 : const bool bIsGeomCollection = 101 10 : wkbFlatten(poGeom->getGeometryType()) == 102 10 : wkbGeometryCollection; 103 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 11 104 : const bool bSrcIs3D = poGeom->Is3D(); 105 : #endif 106 10 : poGeom.reset(poGeom->MakeValid(m_aosMakeValidOptions.List())); 107 10 : if (poGeom) 108 : { 109 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 11 110 : if (!bSrcIs3D && poGeom->Is3D()) 111 : poGeom->flattenTo2D(); 112 : #endif 113 10 : if (!bIsGeomCollection && !m_opts.m_keepLowerDim) 114 : { 115 8 : poGeom.reset( 116 : OGRGeometryFactory::removeLowerDimensionSubGeoms( 117 8 : poGeom.get())); 118 : } 119 20 : poGeom->assignSpatialReference(m_srcLayer.GetLayerDefn() 120 10 : ->GetGeomFieldDefn(i) 121 20 : ->GetSpatialRef()); 122 : } 123 : } 124 297 : if (poGeom) 125 : { 126 295 : poSrcFeature->SetGeomField(i, std::move(poGeom)); 127 : } 128 : } 129 : } 130 : 131 594 : return poSrcFeature; 132 : } 133 : 134 : } // namespace 135 : 136 : #endif // HAVE_GEOS 137 : 138 : /************************************************************************/ 139 : /* GDALVectorMakeValidAlgorithm::CreateAlgLayer() */ 140 : /************************************************************************/ 141 : 142 : std::unique_ptr<OGRLayerWithTranslateFeature> 143 10 : GDALVectorMakeValidAlgorithm::CreateAlgLayer( 144 : [[maybe_unused]] OGRLayer &srcLayer) 145 : { 146 : #ifdef HAVE_GEOS 147 10 : return std::make_unique<GDALVectorMakeValidAlgorithmLayer>(srcLayer, 148 10 : m_opts); 149 : #else 150 : CPLAssert(false); 151 : return nullptr; 152 : #endif 153 : } 154 : 155 : /************************************************************************/ 156 : /* GDALVectorMakeValidAlgorithm::RunStep() */ 157 : /************************************************************************/ 158 : 159 10 : bool GDALVectorMakeValidAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) 160 : { 161 : #ifdef HAVE_GEOS 162 : 163 : #if !(GEOS_VERSION_MAJOR > 3 || \ 164 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)) 165 : if (m_opts.m_method == "structure") 166 : { 167 : ReportError( 168 : CE_Failure, CPLE_NotSupported, 169 : "method = 'structure' requires a build against GEOS >= 3.10"); 170 : return false; 171 : } 172 : #endif 173 : 174 10 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt); 175 : #else 176 : (void)ctxt; 177 : ReportError(CE_Failure, CPLE_NotSupported, 178 : "This algorithm is only supported for builds against GEOS"); 179 : return false; 180 : #endif 181 : } 182 : 183 : GDALVectorMakeValidAlgorithmStandalone:: 184 : ~GDALVectorMakeValidAlgorithmStandalone() = default; 185 : 186 : //! @endcond