Skip to content
Permalink
Browse files

[Geometry checker] Add follow boundaries check

  • Loading branch information
manisandro committed Oct 12, 2017
1 parent 1e49955 commit 13725362977087dc1fd818e04cb8b2b82a635b98
@@ -94,6 +94,7 @@ SET(QGIS_ANALYSIS_SRCS
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
@@ -128,6 +129,7 @@ SET(QGIS_ANALYSIS_MOC_HDRS
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
@@ -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
@@ -79,6 +79,7 @@ QgsGeometryCheckerSetupTab::QgsGeometryCheckerSetupTab( QgisInterface *iface, QD
connect( ui.checkBoxSliverPolygons, &QAbstractButton::toggled, ui.widgetSliverThreshold, &QWidget::setEnabled );
connect( ui.checkBoxSliverArea, &QAbstractButton::toggled, ui.doubleSpinBoxSliverArea, &QWidget::setEnabled );
connect( ui.checkLineLayerIntersection, &QAbstractButton::toggled, ui.comboLineLayerIntersection, &QComboBox::setEnabled );
connect( ui.checkBoxFollowBoundaries, &QAbstractButton::toggled, ui.comboBoxFollowBoundaries, &QComboBox::setEnabled );

for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() )
{
@@ -105,6 +106,7 @@ void QgsGeometryCheckerSetupTab::updateLayers()
}
ui.listWidgetInputLayers->clear();
ui.comboLineLayerIntersection->clear();
ui.comboBoxFollowBoundaries->clear();

// Collect layers
for ( QgsVectorLayer *layer : QgsProject::instance()->layers<QgsVectorLayer *>() )
@@ -124,6 +126,7 @@ void QgsGeometryCheckerSetupTab::updateLayers()
{
item->setIcon( QgsApplication::getThemeIcon( "/mIconPolygonLayer.svg" ) );
ui.comboLineLayerIntersection->addItem( layer->name(), layer->id() );
ui.comboBoxFollowBoundaries->addItem( layer->name(), layer->id() );
}
else
{
@@ -264,6 +267,13 @@ void QgsGeometryCheckerSetupTab::runChecks()
}
}
}
QgsVectorLayer *lineLayerCheckLayer = ui.comboLineLayerIntersection->isEnabled() ? dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboLineLayerIntersection->currentData().toString() ) ) : nullptr;
QgsVectorLayer *followBoundaryCheckLayer = ui.comboBoxFollowBoundaries->isEnabled() ? dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboBoxFollowBoundaries->currentData().toString() ) ) : nullptr;
if ( layers.contains( lineLayerCheckLayer ) || layers.contains( followBoundaryCheckLayer ) )
{
QMessageBox::critical( this, tr( "Error" ), tr( "The test layer set contains a layer selected for a topology check." ) );
return;
}

for ( QgsVectorLayer *layer : layers )
{
@@ -41,9 +41,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<y>-265</y>
<width>626</width>
<height>991</height>
<height>1016</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_4">
@@ -485,29 +485,27 @@
<property name="spacing">
<number>2</number>
</property>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxGaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxDuplicates">
<property name="text">
<string>Check for gaps smaller than (map units sqr.)</string>
<string>Check for duplicates</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxOverlaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxGapArea">
<property name="decimals">
<number>6</number>
</property>
<property name="text">
<string>Check for overlaps smaller than (map units sqr.)</string>
<property name="maximum">
<double>999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="comboLineLayerIntersection">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
@@ -518,22 +516,21 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxDuplicates">
<property name="text">
<string>Check for duplicates</string>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxGaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxCovered">
<property name="text">
<string>Check for features within other features</string>
<string>Check for gaps smaller than (map units sqr.)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxGapArea">
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxOverlapArea">
<property name="decimals">
<number>6</number>
</property>
@@ -542,27 +539,37 @@
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QLabel" name="label">
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxCovered">
<property name="text">
<string>&lt;i&gt;Note: Topology checks are performed in the current map CRS.&lt;/i&gt;</string>
<string>Check for features within other features</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxOverlapArea">
<property name="decimals">
<number>6</number>
<item row="4" column="0">
<widget class="QCheckBox" name="checkPointCoveredByLine">
<property name="text">
<string>Points must be covered by lines</string>
</property>
<property name="maximum">
<double>999999999.000000000000000</double>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkLineLayerIntersection">
<property name="text">
<string>Lines must not intersect with features of layer</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkPointCoveredByLine">
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxOverlaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Points must be covered by lines</string>
<string>Check for overlaps smaller than (map units sqr.)</string>
</property>
</widget>
</item>
@@ -573,15 +580,22 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkLineLayerIntersection">
<item row="10" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Lines must not intersect with features of layer</string>
<string>&lt;i&gt;Note: Topology checks are performed in the current map CRS.&lt;/i&gt;</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="comboLineLayerIntersection">
<item row="8" column="0">
<widget class="QCheckBox" name="checkBoxFollowBoundaries">
<property name="text">
<string>Polygons must follow boundaries of layer</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QComboBox" name="comboBoxFollowBoundaries">
<property name="enabled">
<bool>false</bool>
</property>

0 comments on commit 1372536

Please sign in to comment.
You can’t perform that action at this time.