Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsgeometryduplicatecheck.cpp 3 : : --------------------- 4 : : begin : September 2015 5 : : copyright : (C) 2014 by Sandro Mani / Sourcepole AG 6 : : email : smani at sourcepole dot ch 7 : : *************************************************************************** 8 : : * * 9 : : * This program is free software; you can redistribute it and/or modify * 10 : : * it under the terms of the GNU General Public License as published by * 11 : : * the Free Software Foundation; either version 2 of the License, or * 12 : : * (at your option) any later version. * 13 : : * * 14 : : ***************************************************************************/ 15 : : 16 : : #include "qgsgeometrycheckcontext.h" 17 : : #include "qgsgeometryengine.h" 18 : : #include "qgsgeometryduplicatecheck.h" 19 : : #include "qgsspatialindex.h" 20 : : #include "qgsgeometry.h" 21 : : #include "qgsfeaturepool.h" 22 : : #include "qgsvectorlayer.h" 23 : : 24 : 3 : QString QgsGeometryDuplicateCheckError::duplicatesString( const QMap<QString, QgsFeaturePool *> &featurePools, const QMap<QString, QList<QgsFeatureId>> &duplicates ) 25 : : { 26 : 3 : QStringList str; 27 : 6 : for ( auto it = duplicates.constBegin(); it != duplicates.constEnd(); ++it ) 28 : : { 29 : 3 : str.append( featurePools[it.key()]->layer()->name() + ":" ); 30 : 3 : QStringList ids; 31 : 3 : ids.reserve( it.value().length() ); 32 : 6 : for ( QgsFeatureId id : it.value() ) 33 : : { 34 : 3 : ids.append( QString::number( id ) ); 35 : : } 36 : 3 : str.back() += ids.join( ',' ); 37 : 3 : } 38 : 3 : return str.join( QLatin1String( "; " ) ); 39 : 3 : } 40 : : 41 : : 42 : 1 : void QgsGeometryDuplicateCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const 43 : : { 44 : 1 : QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); 45 : 1 : QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); 46 : 1 : QList<QString> layerIds = featureIds.keys(); 47 : 42 : for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) 48 : : { 49 : : // Ensure each pair of layers only gets compared once: remove the current layer from the layerIds, but add it to the layerList for layerFeaturesB 50 : 41 : layerIds.removeOne( layerFeatureA.layer()->id() ); 51 : : 52 : 41 : QgsGeometry geomA = layerFeatureA.geometry(); 53 : 41 : QgsRectangle bboxA = geomA.boundingBox(); 54 : 41 : std::unique_ptr< QgsGeometryEngine > geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( geomA.constGet(), mContext->tolerance ); 55 : 41 : if ( !geomEngineA->isValid() ) 56 : : { 57 : 2 : messages.append( tr( "Duplicate check failed for (%1): the geometry is invalid" ).arg( layerFeatureA.id() ) ); 58 : 2 : continue; 59 : : } 60 : 39 : QMap<QString, QList<QgsFeatureId>> duplicates; 61 : : 62 : 39 : QgsWkbTypes::GeometryType geomType = geomA.type(); 63 : 39 : QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList<QString>() << layerFeatureA.layer()->id() << layerIds, bboxA, {geomType}, mContext ); 64 : 152 : for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) 65 : : { 66 : : // > : only report overlaps within same layer once 67 : 113 : if ( layerFeatureA.layer()->id() == layerFeatureB.layer()->id() && layerFeatureB.feature().id() >= layerFeatureA.feature().id() ) 68 : : { 69 : 75 : continue; 70 : : } 71 : 38 : QString errMsg; 72 : 38 : QgsGeometry geomB = layerFeatureB.geometry(); 73 : 38 : std::unique_ptr<QgsAbstractGeometry> diffGeom( geomEngineA->symDifference( geomB.constGet(), &errMsg ) ); 74 : 38 : if ( errMsg.isEmpty() && ( !diffGeom || diffGeom->isEmpty() ) ) 75 : : { 76 : 3 : duplicates[layerFeatureB.layer()->id()].append( layerFeatureB.feature().id() ); 77 : 3 : } 78 : 35 : else if ( !errMsg.isEmpty() ) 79 : : { 80 : 2 : messages.append( tr( "Duplicate check failed for (%1, %2): %3" ).arg( layerFeatureA.id(), layerFeatureB.id(), errMsg ) ); 81 : 2 : } 82 : 38 : } 83 : 39 : if ( !duplicates.isEmpty() ) 84 : : { 85 : 3 : errors.append( new QgsGeometryDuplicateCheckError( this, layerFeatureA, geomA.constGet()->centroid(), featurePools, duplicates ) ); 86 : 3 : } 87 : 41 : } 88 : 1 : } 89 : : 90 : 1 : void QgsGeometryDuplicateCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const 91 : : { 92 : 1 : QgsFeaturePool *featurePoolA = featurePools[ error->layerId() ]; 93 : 1 : QgsFeature featureA; 94 : 1 : if ( !featurePoolA->getFeature( error->featureId(), featureA ) ) 95 : : { 96 : 0 : error->setObsolete(); 97 : 0 : return; 98 : : } 99 : : 100 : 1 : if ( method == NoChange ) 101 : : { 102 : 0 : error->setFixed( method ); 103 : 0 : } 104 : 1 : else if ( method == RemoveDuplicates ) 105 : : { 106 : 1 : QgsGeometryCheckerUtils::LayerFeature layerFeatureA( featurePoolA, featureA, mContext, true ); 107 : 1 : std::unique_ptr< QgsGeometryEngine > geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( layerFeatureA.geometry().constGet(), mContext->tolerance ); 108 : : 109 : 1 : QgsGeometryDuplicateCheckError *duplicateError = static_cast<QgsGeometryDuplicateCheckError *>( error ); 110 : 1 : const QMap<QString, QList<QgsFeatureId>> duplicates = duplicateError->duplicates(); 111 : 2 : for ( auto it = duplicates.constBegin(); it != duplicates.constEnd(); ++it ) 112 : : { 113 : 1 : const QString layerIdB = it.key(); 114 : 1 : QgsFeaturePool *featurePoolB = featurePools[ layerIdB ]; 115 : 1 : const QList< QgsFeatureId > ids = it.value(); 116 : 2 : for ( QgsFeatureId idB : ids ) 117 : : { 118 : 1 : QgsFeature featureB; 119 : 1 : if ( !featurePoolB->getFeature( idB, featureB ) ) 120 : : { 121 : 0 : continue; 122 : : } 123 : 1 : QgsGeometryCheckerUtils::LayerFeature layerFeatureB( featurePoolB, featureB, mContext, true ); 124 : 1 : std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngineA->symDifference( layerFeatureB.geometry().constGet() ) ); 125 : 1 : if ( !diffGeom || diffGeom->isEmpty() ) 126 : : { 127 : 1 : featurePoolB->deleteFeature( featureB.id() ); 128 : 1 : changes[layerIdB][idB].append( Change( ChangeFeature, ChangeRemoved ) ); 129 : 1 : } 130 : 1 : } 131 : 1 : } 132 : 1 : error->setFixed( method ); 133 : 1 : } 134 : : else 135 : : { 136 : 0 : error->setFixFailed( tr( "Unknown method" ) ); 137 : : } 138 : 1 : } 139 : : 140 : 1 : QStringList QgsGeometryDuplicateCheck::resolutionMethods() const 141 : : { 142 : 2 : static QStringList methods = QStringList() 143 : 1 : << tr( "No action" ) 144 : 1 : << tr( "Remove duplicates" ); 145 : 1 : return methods; 146 : 0 : } 147 : : 148 : 0 : QString QgsGeometryDuplicateCheck::factoryId() 149 : : { 150 : 0 : return QStringLiteral( "QgsGeometryDuplicateCheck" ); 151 : : } 152 : : 153 : 0 : QgsGeometryCheck::CheckType QgsGeometryDuplicateCheck::factoryCheckType() 154 : : { 155 : 0 : return QgsGeometryCheck::FeatureCheck; 156 : : }