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