Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL 4 : * Purpose: "gdal vector dissolve" 5 : * Author: Dan Baston 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2025, ISciences LLC 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "gdalalg_vector_dissolve.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 : /************************************************************************/ 27 : /* GDALVectorDissolveAlgorithm() */ 28 : /************************************************************************/ 29 : 30 46 : GDALVectorDissolveAlgorithm::GDALVectorDissolveAlgorithm(bool standaloneStep) 31 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL, 32 46 : standaloneStep, m_opts) 33 : { 34 46 : } 35 : 36 : #ifdef HAVE_GEOS 37 : 38 : namespace 39 : { 40 : 41 : /************************************************************************/ 42 : /* GDALVectorDissolveAlgorithmLayer */ 43 : /************************************************************************/ 44 : 45 : class GDALVectorDissolveAlgorithmLayer final 46 : : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorDissolveAlgorithm> 47 : { 48 : public: 49 4 : GDALVectorDissolveAlgorithmLayer( 50 : OGRLayer &oSrcLayer, const GDALVectorDissolveAlgorithm::Options &opts) 51 4 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorDissolveAlgorithm>( 52 4 : oSrcLayer, opts) 53 : { 54 4 : } 55 : 56 : protected: 57 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature; 58 : 59 : std::unique_ptr<OGRFeature> 60 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override; 61 : 62 : private: 63 : }; 64 : 65 : /************************************************************************/ 66 : /* TranslateFeature() */ 67 : /************************************************************************/ 68 : 69 2 : std::unique_ptr<OGRGeometry> LineMerge(const OGRMultiLineString *poGeom) 70 : { 71 2 : GEOSContextHandle_t hContext = OGRGeometry::createGEOSContext(); 72 2 : GEOSGeometry *hGeosGeom = poGeom->exportToGEOS(hContext); 73 2 : if (!hGeosGeom) 74 : { 75 0 : OGRGeometry::freeGEOSContext(hContext); 76 0 : return nullptr; 77 : } 78 : 79 2 : GEOSGeometry *hGeosResult = GEOSLineMerge_r(hContext, hGeosGeom); 80 2 : GEOSGeom_destroy_r(hContext, hGeosGeom); 81 : 82 2 : if (!hGeosResult) 83 : { 84 0 : OGRGeometry::freeGEOSContext(hContext); 85 0 : return nullptr; 86 : } 87 : 88 : std::unique_ptr<OGRGeometry> ret( 89 4 : OGRGeometryFactory::createFromGEOS(hContext, hGeosResult)); 90 2 : GEOSGeom_destroy_r(hContext, hGeosResult); 91 2 : OGRGeometry::freeGEOSContext(hContext); 92 : 93 2 : if (ret) 94 : { 95 2 : const auto eRetType = wkbFlatten(ret->getGeometryType()); 96 2 : if (eRetType != wkbLineString && eRetType != wkbMultiLineString) 97 : { 98 0 : CPLError(CE_Failure, CPLE_AppDefined, 99 : "LineMerge returned a geometry of type %s, expected " 100 : "LineString or MultiLineString", 101 : OGRGeometryTypeToName(eRetType)); 102 0 : return nullptr; 103 : } 104 : } 105 : 106 2 : return ret; 107 : } 108 : 109 4 : std::unique_ptr<OGRFeature> GDALVectorDissolveAlgorithmLayer::TranslateFeature( 110 : std::unique_ptr<OGRFeature> poSrcFeature) const 111 : { 112 4 : const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount(); 113 8 : for (int iGeomField = 0; iGeomField < nGeomFieldCount; ++iGeomField) 114 : { 115 4 : if (IsSelectedGeomField(iGeomField)) 116 : { 117 4 : if (auto poGeom = std::unique_ptr<OGRGeometry>( 118 4 : poSrcFeature->StealGeometry(iGeomField))) 119 : { 120 4 : poGeom.reset(poGeom->UnaryUnion()); 121 4 : if (!poGeom) 122 : { 123 0 : CPLError(CE_Failure, CPLE_AppDefined, 124 : "Failed to perform union of geometry on feature " 125 : "%" PRId64, 126 0 : static_cast<int64_t>(poSrcFeature->GetFID())); 127 0 : return nullptr; 128 : } 129 : 130 4 : const auto eResultType = wkbFlatten(poGeom->getGeometryType()); 131 : 132 4 : if (eResultType == wkbMultiLineString) 133 : { 134 1 : poGeom = LineMerge(poGeom->toMultiLineString()); 135 1 : if (!poGeom) 136 : { 137 0 : CPLError(CE_Failure, CPLE_AppDefined, 138 : "Failed to merge lines of feature %" PRId64, 139 0 : static_cast<int64_t>(poSrcFeature->GetFID())); 140 0 : return nullptr; 141 : } 142 : } 143 3 : else if (eResultType == wkbGeometryCollection) 144 : { 145 : OGRGeometryCollection *poColl = 146 1 : poGeom->toGeometryCollection(); 147 : 148 1 : OGRMultiLineString oMLS; 149 : 150 1 : const auto nGeoms = poColl->getNumGeometries(); 151 4 : for (int i = nGeoms - 1; i >= 0; i--) 152 : { 153 3 : const auto eComponentType = wkbFlatten( 154 : poColl->getGeometryRef(i)->getGeometryType()); 155 3 : if (eComponentType == wkbLineString) 156 : { 157 4 : oMLS.addGeometryDirectly(poColl->stealGeometry(i) 158 : .release() 159 2 : ->toLineString()); 160 : } 161 : } 162 : 163 1 : if (oMLS.getNumGeometries() > 0) 164 : { 165 : std::unique_ptr<OGRGeometry> poMerged = 166 1 : LineMerge(&oMLS); 167 1 : if (!poMerged) 168 : { 169 0 : CPLError( 170 : CE_Failure, CPLE_AppDefined, 171 : "Failed to merge lines of feature %" PRId64, 172 0 : static_cast<int64_t>(poSrcFeature->GetFID())); 173 0 : return nullptr; 174 : } 175 : 176 : const auto eMergedType = 177 1 : wkbFlatten(poMerged->getGeometryType()); 178 1 : if (eMergedType == wkbLineString) 179 : { 180 1 : poColl->addGeometry(std::move(poMerged)); 181 : } 182 : else // eMergedType == wkbMultiLineString 183 : { 184 : OGRMultiLineString *poMergedMLS = 185 0 : poMerged->toMultiLineString(); 186 : const auto nMergedGeoms = 187 0 : poMergedMLS->getNumGeometries(); 188 0 : for (int i = nMergedGeoms - 1; i >= 0; i--) 189 : { 190 0 : poColl->addGeometryDirectly( 191 0 : poMergedMLS->stealGeometry(i) 192 : .release() 193 0 : ->toLineString()); 194 : } 195 : } 196 : } 197 : } 198 : 199 4 : if (poGeom) 200 : { 201 8 : poGeom->assignSpatialReference( 202 8 : m_srcLayer.GetLayerDefn() 203 4 : ->GetGeomFieldDefn(iGeomField) 204 8 : ->GetSpatialRef()); 205 4 : poSrcFeature->SetGeomField(iGeomField, std::move(poGeom)); 206 : } 207 : } 208 : } 209 : } 210 : 211 4 : return poSrcFeature; 212 : } 213 : 214 : } // namespace 215 : 216 : #endif // HAVE_GEOS 217 : 218 : /************************************************************************/ 219 : /* GDALVectorDissolveAlgorithm::CreateAlgLayer() */ 220 : /************************************************************************/ 221 : 222 : std::unique_ptr<OGRLayerWithTranslateFeature> 223 4 : GDALVectorDissolveAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer) 224 : { 225 : #ifdef HAVE_GEOS 226 4 : return std::make_unique<GDALVectorDissolveAlgorithmLayer>(srcLayer, m_opts); 227 : #else 228 : CPLAssert(false); 229 : return nullptr; 230 : #endif 231 : } 232 : 233 : /************************************************************************/ 234 : /* GDALVectorDissolveAlgorithm::RunStep() */ 235 : /************************************************************************/ 236 : 237 4 : bool GDALVectorDissolveAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) 238 : { 239 : #ifdef HAVE_GEOS 240 4 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt); 241 : #else 242 : (void)ctxt; 243 : ReportError(CE_Failure, CPLE_NotSupported, 244 : "This algorithm is only supported for builds against GEOS"); 245 : return false; 246 : #endif 247 : } 248 : 249 : GDALVectorDissolveAlgorithmStandalone:: 250 : ~GDALVectorDissolveAlgorithmStandalone() = default; 251 : 252 : //! @endcond