Skip to content

Commit

Permalink
Merge pull request #5410 from manisandro/geomchecker
Browse files Browse the repository at this point in the history
[FEATURE][Geometry checker] Support checking multiple layers at once, add new checks
  • Loading branch information
manisandro authored Oct 23, 2017
2 parents 46a6f25 + 8421a7e commit ea0e09b
Show file tree
Hide file tree
Showing 121 changed files with 5,572 additions and 2,472 deletions.
8 changes: 8 additions & 0 deletions python/core/geometry/qgsgeometryengine.sip
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ class QgsGeometryEngine
%Docstring
Calculate the combination of this and ``geom``.

.. versionadded:: 3.0
:rtype: QgsAbstractGeometry
%End

virtual QgsAbstractGeometry *combine( const QList<QgsAbstractGeometry *> &geomList, QString *errorMsg ) const = 0 /Factory/;
%Docstring
Calculate the combination of this and ``geometries``.

.. versionadded:: 3.0
:rtype: QgsAbstractGeometry
%End
Expand Down
1 change: 1 addition & 0 deletions python/gui/gui_auto.sip
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
%Include qgsabstractdatasourcewidget.sip
%Include qgssourceselectprovider.sip
%Include qgssourceselectproviderregistry.sip
%Include qgsvscrollarea.sip
%Include attributetable/qgsfeaturemodel.sip
%Include auth/qgsauthauthoritieseditor.sip
%Include auth/qgsauthcertificateinfo.sip
Expand Down
42 changes: 42 additions & 0 deletions python/gui/qgsvscrollarea.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsvscrollarea.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsVScrollArea : QScrollArea
{
%Docstring
QgsVScrollArea is a QScrollArea subclass which only displays a vertical
scrollbar and fits the width to the contents.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvscrollarea.h"
%End
public:

QgsVScrollArea( QWidget *parent = 0 );
%Docstring
QgsVScrollArea
\param parent The parent widget
%End

virtual bool eventFilter( QObject *o, QEvent *e );

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsvscrollarea.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
54 changes: 54 additions & 0 deletions src/analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,33 @@ SET(QGIS_ANALYSIS_SRCS
network/qgsnetworkdistancestrategy.cpp
network/qgsvectorlayerdirector.cpp
network/qgsgraphanalyzer.cpp

vector/geometry_checker/qgsfeaturepool.cpp
vector/geometry_checker/qgsgeometrychecker.cpp
vector/geometry_checker/qgsgeometryanglecheck.cpp
vector/geometry_checker/qgsgeometryareacheck.cpp
vector/geometry_checker/qgsgeometrycheck.cpp
vector/geometry_checker/qgsgeometrychecker.cpp
vector/geometry_checker/qgsgeometrycheckerutils.cpp
vector/geometry_checker/qgsgeometrycontainedcheck.cpp
vector/geometry_checker/qgsgeometrydanglecheck.cpp
vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.cpp
vector/geometry_checker/qgsgeometryduplicatecheck.cpp
vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp
vector/geometry_checker/qgsgeometrygapcheck.cpp
vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp
vector/geometry_checker/qgsgeometryholecheck.cpp
vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp
vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp
vector/geometry_checker/qgsgeometrymultipartcheck.cpp
vector/geometry_checker/qgsgeometryoverlapcheck.cpp
vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp
vector/geometry_checker/qgsgeometrypointinpolygoncheck.cpp
vector/geometry_checker/qgsgeometrysegmentlengthcheck.cpp
vector/geometry_checker/qgsgeometryselfcontactcheck.cpp
vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp
vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp
vector/geometry_checker/qgsgeometrytypecheck.cpp
)

SET(QGIS_ANALYSIS_MOC_HDRS
Expand All @@ -91,6 +118,33 @@ SET(QGIS_ANALYSIS_MOC_HDRS

network/qgsgraphdirector.h
network/qgsvectorlayerdirector.h

vector/geometry_checker/qgsfeaturepool.h
vector/geometry_checker/qgsgeometrychecker.h
vector/geometry_checker/qgsgeometryanglecheck.h
vector/geometry_checker/qgsgeometryareacheck.h
vector/geometry_checker/qgsgeometrychecker.h
vector/geometry_checker/qgsgeometrycheckerutils.h
vector/geometry_checker/qgsgeometrycheck.h
vector/geometry_checker/qgsgeometrycontainedcheck.h
vector/geometry_checker/qgsgeometrydanglecheck.h
vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h
vector/geometry_checker/qgsgeometryduplicatecheck.h
vector/geometry_checker/qgsgeometryduplicatenodescheck.h
vector/geometry_checker/qgsgeometryfollowboundariescheck.h
vector/geometry_checker/qgsgeometrygapcheck.h
vector/geometry_checker/qgsgeometryholecheck.h
vector/geometry_checker/qgsgeometrylineintersectioncheck.h
vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h
vector/geometry_checker/qgsgeometrymultipartcheck.h
vector/geometry_checker/qgsgeometryoverlapcheck.h
vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h
vector/geometry_checker/qgsgeometrypointinpolygoncheck.h
vector/geometry_checker/qgsgeometrysegmentlengthcheck.h
vector/geometry_checker/qgsgeometryselfcontactcheck.h
vector/geometry_checker/qgsgeometryselfintersectioncheck.h
vector/geometry_checker/qgsgeometrysliverpolygoncheck.h
vector/geometry_checker/qgsgeometrytypecheck.h
)

INCLUDE_DIRECTORIES(SYSTEM ${SPATIALITE_INCLUDE_DIR})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
#include <QMutexLocker>
#include <limits>

QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, bool selectedOnly )
QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform, bool selectedOnly )
: mFeatureCache( CACHE_SIZE )
, mLayer( layer )
, mLayerToMapUnits( layerToMapUnits )
, mLayerToMapTransform( layerToMapTransform )
, mSelectedOnly( selectedOnly )
{
if ( selectedOnly )
Expand All @@ -46,7 +48,7 @@ QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, bool selectedOnly )
QgsFeatureIterator it = layer->getFeatures( req );
while ( it.nextFeature( feature ) )
{
if ( feature.geometry() )
if ( mFeatureIds.contains( feature.id() ) && feature.geometry() )
{
mIndex.insertFeature( feature );
}
Expand Down Expand Up @@ -103,6 +105,9 @@ void QgsFeaturePool::addFeature( QgsFeature &feature )

void QgsFeaturePool::updateFeature( QgsFeature &feature )
{
QgsFeature origFeature;
get( feature.id(), origFeature );

QgsGeometryMap geometryMap;
geometryMap.insert( feature.id(), QgsGeometry( feature.geometry().geometry()->clone() ) );
QgsChangedAttributesMap changedAttributesMap;
Expand All @@ -118,23 +123,27 @@ void QgsFeaturePool::updateFeature( QgsFeature &feature )
mLayer->dataProvider()->changeAttributeValues( changedAttributesMap );
mLayerMutex.unlock();
mIndexMutex.lock();
mIndex.deleteFeature( feature );
mIndex.deleteFeature( origFeature );
mIndex.insertFeature( feature );
mIndexMutex.unlock();
}

void QgsFeaturePool::deleteFeature( QgsFeature &feature )
void QgsFeaturePool::deleteFeature( const QgsFeatureId &fid )
{
mIndexMutex.lock();
mIndex.deleteFeature( feature );
mIndexMutex.unlock();
QgsFeature origFeature;
if ( get( fid, origFeature ) )
{
mIndexMutex.lock();
mIndex.deleteFeature( origFeature );
mIndexMutex.unlock();
}
mLayerMutex.lock();
mFeatureCache.remove( feature.id() );
mLayer->dataProvider()->deleteFeatures( QgsFeatureIds() << feature.id() );
mFeatureCache.remove( origFeature.id() );
mLayer->dataProvider()->deleteFeatures( QgsFeatureIds() << fid );
mLayerMutex.unlock();
}

QgsFeatureIds QgsFeaturePool::getIntersects( const QgsRectangle &rect )
QgsFeatureIds QgsFeaturePool::getIntersects( const QgsRectangle &rect ) const
{
QMutexLocker lock( &mIndexMutex );
return QgsFeatureIds::fromList( mIndex.intersects( rect ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,36 @@
* *
***************************************************************************/

#define SIP_NO_FILE

#ifndef QGS_FEATUREPOOL_H
#define QGS_FEATUREPOOL_H

#include <QCache>
#include <QLinkedList>
#include <QMap>
#include <QMutex>
#include "qgis_analysis.h"
#include "qgsfeature.h"
#include "qgsspatialindex.h"
#include "qgsgeometrycheckerutils.h"

class QgsVectorLayer;

class QgsFeaturePool
class ANALYSIS_EXPORT QgsFeaturePool : public QObject
{
Q_OBJECT
public:
QgsFeaturePool( QgsVectorLayer *layer, bool selectedOnly = false );
QgsFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform, bool selectedOnly = false );
bool get( QgsFeatureId id, QgsFeature &feature );
void addFeature( QgsFeature &feature );
void updateFeature( QgsFeature &feature );
void deleteFeature( QgsFeature &feature );
QgsFeatureIds getIntersects( const QgsRectangle &rect );
void deleteFeature( const QgsFeatureId &fid );
QgsFeatureIds getIntersects( const QgsRectangle &rect ) const;
QgsVectorLayer *getLayer() const { return mLayer; }
const QgsFeatureIds &getFeatureIds() const { return mFeatureIds; }
double getLayerToMapUnits() const { return mLayerToMapUnits; }
const QgsCoordinateTransform &getLayerToMapTransform() const { return mLayerToMapTransform; }
bool getSelectedOnly() const { return mSelectedOnly; }
void clearLayer() { mLayer = nullptr; }

Expand All @@ -58,8 +64,10 @@ class QgsFeaturePool
QgsVectorLayer *mLayer = nullptr;
QgsFeatureIds mFeatureIds;
QMutex mLayerMutex;
QMutex mIndexMutex;
mutable QMutex mIndexMutex;
QgsSpatialIndex mIndex;
double mLayerToMapUnits;
QgsCoordinateTransform mLayerToMapTransform;
bool mSelectedOnly;

bool getTouchingWithSharedEdge( QgsFeature &feature, QgsFeatureId &touchingId, const double & ( *comparator )( const double &, const double & ), double init );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,27 @@

#include "qgsgeometryanglecheck.h"
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
#include "qgsfeaturepool.h"

void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsGeometry g = feature.geometry();
const QgsAbstractGeometry *geom = g.geometry();
const QgsAbstractGeometry *geom = layerFeature.geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
bool closed = false;
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing, &closed );
// Less than three points, no angles to check
if ( nVerts < 3 )
{
continue;
}
for ( int iVert = 0; iVert < nVerts; ++iVert )
for ( int iVert = !closed; iVert < nVerts - !closed; ++iVert )
{
const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
Expand All @@ -57,27 +52,28 @@ void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &error
continue;
}

double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
double angle = qAcos( v21 * v23 ) / M_PI * 180.0;
if ( angle < mMinAngle )
{
errors.append( new QgsGeometryCheckError( this, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
errors.append( new QgsGeometryCheckError( this, layerFeature, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
}
}
}
}
}
}

void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
if ( !featurePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsGeometry g = feature.geometry();
QgsAbstractGeometry *geometry = g.geometry();
QgsGeometry featureGeometry = feature.geometry();
QgsAbstractGeometry *geometry = featureGeometry.geometry();
QgsVertexId vidx = error->vidx();

// Check if point still exists
Expand Down Expand Up @@ -132,15 +128,16 @@ void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method,
}
else
{
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance()
&& QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) &&
changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
// Avoid duplicate nodes as result of deleting spike vertex
if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < mContext->tolerance &&
QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) &&
geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2
{
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
}
feature.setGeometry( g );
mFeaturePool->updateFeature( feature );
feature.setGeometry( featureGeometry );
featurePool->updateFeature( feature );
error->setFixed( method );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,31 @@
* *
***************************************************************************/

#define SIP_NO_FILE

#ifndef QGS_GEOMETRY_ANGLE_CHECK_H
#define QGS_GEOMETRY_ANGLE_CHECK_H

#include "qgsgeometrycheck.h"

class QgsGeometryAngleCheck : public QgsGeometryCheck
class ANALYSIS_EXPORT QgsGeometryAngleCheck : public QgsGeometryCheck
{
Q_OBJECT

public:
QgsGeometryAngleCheck( QgsFeaturePool *featurePool, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, featurePool )
, mMinAngle( minAngle )
QgsGeometryAngleCheck( QgsGeometryCheckerContext *context, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context )
, mMinAngle( minAngle )
{}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override;
void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override;
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( "Minimal angle" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryAngleCheck" ); }
private:

enum ResolutionMethod { DeleteNode, NoChange };

private:
double mMinAngle;
};

Expand Down
Loading

0 comments on commit ea0e09b

Please sign in to comment.