Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
with
281 additions
and 48 deletions.
- +2 −0 src/analysis/CMakeLists.txt
- +106 −0 src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp
- +42 −0 src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h
- +10 −0 src/plugins/geometry_checker/qgsgeometrycheckersetuptab.cpp
- +62 −48 src/plugins/geometry_checker/qgsgeometrycheckersetuptab.ui
- +32 −0 src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp
- +23 −0 tests/src/geometry_checker/testqgsgeometrychecks.cpp
- BIN tests/testdata/geometry_checker/follow_ref.dbf
- +1 −0 tests/testdata/geometry_checker/follow_ref.prj
- +1 −0 tests/testdata/geometry_checker/follow_ref.qpj
- BIN tests/testdata/geometry_checker/follow_ref.shp
- BIN tests/testdata/geometry_checker/follow_ref.shx
- BIN tests/testdata/geometry_checker/follow_subj.dbf
- +1 −0 tests/testdata/geometry_checker/follow_subj.prj
- +1 −0 tests/testdata/geometry_checker/follow_subj.qpj
- BIN tests/testdata/geometry_checker/follow_subj.shp
- BIN tests/testdata/geometry_checker/follow_subj.shx
@@ -0,0 +1,106 @@ | ||
/*************************************************************************** | ||
qgsgeometryfollowboundariescheck.cpp | ||
--------------------- | ||
begin : September 2017 | ||
copyright : (C) 2017 by Sandro Mani / Sourcepole AG | ||
email : smani at sourcepole dot ch | ||
*************************************************************************** | ||
* * | ||
* This program is free software; you can redistribute it and/or modify * | ||
* it under the terms of the GNU General Public License as published by * | ||
* the Free Software Foundation; either version 2 of the License, or * | ||
* (at your option) any later version. * | ||
* * | ||
***************************************************************************/ | ||
|
||
#include "qgscrscache.h" | ||
#include "qgsgeometryfollowboundariescheck.h" | ||
#include "qgsgeometryengine.h" | ||
#include "../qgsgeometrysnapper.h" | ||
|
||
|
||
QgsGeometryFollowBoundariesCheck::QgsGeometryFollowBoundariesCheck( QgsGeometryCheckerContext *context, QgsVectorLayer *checkLayer ) | ||
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PolygonGeometry}, context ) | ||
{ | ||
mCheckLayer = checkLayer; | ||
if ( mCheckLayer ) | ||
{ | ||
mIndex = new QgsSpatialIndex( *mCheckLayer->dataProvider() ); | ||
} | ||
} | ||
|
||
QgsGeometryFollowBoundariesCheck::~QgsGeometryFollowBoundariesCheck() | ||
{ | ||
delete mIndex; | ||
} | ||
|
||
void QgsGeometryFollowBoundariesCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const | ||
{ | ||
if ( !mIndex || !mCheckLayer ) | ||
{ | ||
return; | ||
} | ||
|
||
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; | ||
featureIds.remove( mCheckLayer->id() ); // Don't check layer against itself | ||
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); | ||
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) | ||
{ | ||
const QgsAbstractGeometry *geom = layerFeature.geometry(); | ||
|
||
// The geometry to crs of the check layer | ||
QgsCoordinateTransform crst = QgsCoordinateTransformCache::instance()->transform( layerFeature.layer().crs().authid(), mCheckLayer->crs().authid() ); | ||
QScopedPointer<QgsAbstractGeometry> geomt( geom->clone() ); | ||
geomt->transform( crst ); | ||
|
||
QSharedPointer<QgsGeometryEngine> geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geomt.data(), mContext->tolerance ); | ||
|
||
// Get potential reference features | ||
QgsRectangle searchBounds = geomt->boundingBox(); | ||
searchBounds.grow( mContext->tolerance ); | ||
QgsFeatureIds refFeatureIds = mIndex->intersects( searchBounds ).toSet(); | ||
|
||
QgsFeatureRequest refFeatureRequest = QgsFeatureRequest().setFilterFids( refFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); | ||
QgsFeatureIterator refFeatureIt = mCheckLayer->getFeatures( refFeatureRequest ); | ||
|
||
if ( refFeatureIds.isEmpty() ) | ||
{ | ||
// If no potential reference features are found, the geometry is definitely not following boundaries of reference layer features | ||
errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); | ||
} | ||
else | ||
{ | ||
// All reference features must be either contained or disjoint from tested geometry | ||
QgsFeature refFeature; | ||
while ( refFeatureIt.nextFeature( refFeature ) ) | ||
{ | ||
QgsAbstractGeometry *refGeom = refFeature.geometry().geometry(); | ||
QSharedPointer<QgsGeometryEngine> refgeomEngine = QgsGeometryCheckerUtils::createGeomEngine( refGeom, mContext->tolerance ); | ||
QScopedPointer<QgsAbstractGeometry> reducedRefGeom( refgeomEngine->buffer( -mContext->tolerance, 0 ) ); | ||
if ( !( geomEngine->contains( reducedRefGeom.data() ) || geomEngine->disjoint( reducedRefGeom.data() ) ) ) | ||
{ | ||
errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
void QgsGeometryFollowBoundariesCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const | ||
{ | ||
if ( method == NoChange ) | ||
{ | ||
error->setFixed( method ); | ||
} | ||
else | ||
{ | ||
error->setFixFailed( tr( "Unknown method" ) ); | ||
} | ||
} | ||
|
||
QStringList QgsGeometryFollowBoundariesCheck::getResolutionMethods() const | ||
{ | ||
static QStringList methods = QStringList() << tr( "No action" ); | ||
return methods; | ||
} |
@@ -0,0 +1,42 @@ | ||
/*************************************************************************** | ||
qgsgeometryfollowboundariescheck.h | ||
--------------------- | ||
begin : September 2017 | ||
copyright : (C) 2017 by Sandro Mani / Sourcepole AG | ||
email : smani at sourcepole dot ch | ||
*************************************************************************** | ||
* * | ||
* This program is free software; you can redistribute it and/or modify * | ||
* it under the terms of the GNU General Public License as published by * | ||
* the Free Software Foundation; either version 2 of the License, or * | ||
* (at your option) any later version. * | ||
* * | ||
***************************************************************************/ | ||
|
||
#ifndef QGSGEOMETRYFOLLOWBOUNDARIESCHECK_H | ||
#define QGSGEOMETRYFOLLOWBOUNDARIESCHECK_H | ||
|
||
#include "qgsgeometrycheck.h" | ||
|
||
class QgsSpatialIndex; | ||
|
||
|
||
class ANALYSIS_EXPORT QgsGeometryFollowBoundariesCheck : public QgsGeometryCheck | ||
{ | ||
Q_OBJECT | ||
|
||
public: | ||
QgsGeometryFollowBoundariesCheck( QgsGeometryCheckerContext *context, QgsVectorLayer *checkLayer ); | ||
~QgsGeometryFollowBoundariesCheck(); | ||
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override; | ||
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override; | ||
QStringList getResolutionMethods() const override; | ||
QString errorDescription() const override { return tr( "Polygon does not follow boundaries" ); } | ||
QString errorName() const override { return QStringLiteral( "QgsGeometryFollowBoundariesCheck" ); } | ||
private: | ||
enum ResolutionMethod { NoChange }; | ||
QgsVectorLayer *mCheckLayer; | ||
QgsSpatialIndex *mIndex = nullptr; | ||
}; | ||
|
||
#endif // QGSGEOMETRYFOLLOWBOUNDARIESCHECK_H |
Oops, something went wrong.