Skip to content
Permalink
Browse files

Merge pull request #4232 from manisandro/geomchecker

Geometry checker: Add self-contact check
  • Loading branch information
manisandro committed Mar 7, 2017
2 parents 9754590 + db138ff commit 33d6b51beadecababafb3a1494c30a5b3b9115e2
@@ -18,6 +18,7 @@ SET (geometrychecker_SRCS
checks/qgsgeometrycontainedcheck.cpp
checks/qgsgeometryoverlapcheck.cpp
checks/qgsgeometrysegmentlengthcheck.cpp
checks/qgsgeometryselfcontactcheck.cpp
checks/qgsgeometryselfintersectioncheck.cpp
checks/qgsgeometrytypecheck.cpp
ui/qgsgeometrycheckerdialog.cpp
@@ -52,6 +53,7 @@ SET (geometrychecker_MOC_HDRS
checks/qgsgeometrycontainedcheck.h
checks/qgsgeometryoverlapcheck.h
checks/qgsgeometrysegmentlengthcheck.h
checks/qgsgeometryselfcontactcheck.h
checks/qgsgeometryselfintersectioncheck.h
checks/qgsgeometrysliverpolygoncheck.h
checks/qgsgeometrytypecheck.h
@@ -0,0 +1,101 @@
/***************************************************************************
* qgsgeometryselfcontactcheck.cpp *
* ------------------- *
* copyright : (C) 2017 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/

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

void QgsGeometrySelfContactCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
double tolerance = QgsGeometryCheckPrecision::tolerance();
foreach ( const QgsFeatureId &featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometry *geom = feature.geometry().geometry();

for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
// Test for self-contacts
int n = geom->vertexCount( iPart, iRing );
bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) );

// Geometry ring without duplicate nodes
QVector<int> vtxMap;
QVector<QgsPointV2> ring;
vtxMap.append( 0 );
ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) );
for ( int i = 1; i < n; ++i )
{
QgsPointV2 p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) );
if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > tolerance * tolerance )
{
vtxMap.append( i );
ring.append( p );
}
}
while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < tolerance * tolerance )
{
vtxMap.pop_back();
ring.pop_back();
}
if ( isClosed )
{
vtxMap.append( n - 1 );
ring.append( ring.front() );
}
n = ring.size();

// For each vertex, check whether it lies on a segment
for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert )
{
const QgsPointV2 &p = ring[iVert];
for ( int i = 0, j = 1; j < n; i = j++ )
{
if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) )
{
continue;
}
const QgsPointV2 &si = ring[i];
const QgsPointV2 &sj = ring[j];
QgsPointV2 q = QgsGeometryUtils::projPointOnSegment( p, si, sj );
if ( QgsGeometryUtils::sqrDistance2D( p, q ) < tolerance * tolerance )
{
errors.append( new QgsGeometryCheckError( this, featureid, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) );
break; // No need to report same contact on different segments multiple times
}
}
}
}
}
}
}

void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes & /*changes*/ ) const
{
if ( method == NoChange )
{
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}

QStringList QgsGeometrySelfContactCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "No action" );
return methods;
}
@@ -0,0 +1,29 @@
/***************************************************************************
* qgsgeometryselfcontactcheck.h *
* ------------------- *
* copyright : (C) 2017 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/

#ifndef QGS_GEOMETRY_SELFCONTACT_CHECK_H
#define QGS_GEOMETRY_SELFCONTACT_CHECK_H

#include "qgsgeometrycheck.h"

class QgsGeometrySelfContactCheck : public QgsGeometryCheck
{
Q_OBJECT

public:
QgsGeometrySelfContactCheck( QgsFeaturePool *featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = 0, const QgsFeatureIds &ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError *error, int method, int, Changes & ) const;
QStringList getResolutionMethods() const;
QString errorDescription() const { return tr( "Self contact" ); }
QString errorName() const { return "QgsGeometrySelfContactCheck"; }
private:
enum ResolutionMethod { NoChange };
};

#endif // QGS_GEOMETRY_SELFCONTACT_CHECK_H
@@ -25,8 +25,6 @@
bool QgsGeometrySelfIntersectionCheckError::isEqual( QgsGeometryCheckError *other ) const
{
return QgsGeometryCheckError::isEqual( other ) &&
other->featureId() == featureId() &&
other->vidx() == vidx() &&
static_cast<QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment1 == intersection().segment1 &&
static_cast<QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment2 == intersection().segment2;
}
@@ -27,6 +27,7 @@
#include "checks/qgsgeometrymultipartcheck.h"
#include "checks/qgsgeometryoverlapcheck.h"
#include "checks/qgsgeometrysegmentlengthcheck.h"
#include "checks/qgsgeometryselfcontactcheck.h"
#include "checks/qgsgeometryselfintersectioncheck.h"
#include "checks/qgsgeometrysliverpolygoncheck.h"
#include "checks/qgsgeometrytypecheck.h"
@@ -357,6 +358,34 @@ REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT<QgsGeometrySegment

///////////////////////////////////////////////////////////////////////////////

template<> void QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const
{
ui.checkBoxSelfContacts->setChecked( QgsSettings().value( sSettingsGroup + "checkSelfContacts" ).toBool() );
}

template<> bool QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
{
ui.checkBoxSelfContacts->setEnabled( geomType == QgsWkbTypes::PolygonGeometry || geomType == QgsWkbTypes::LineGeometry );
return ui.checkBoxSelfContacts->isEnabled();
}

template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
{
QgsSettings().setValue( sSettingsGroup + "checkSelfContacts", ui.checkBoxSelfContacts->isChecked() );
if ( ui.checkBoxSelfContacts->isEnabled() && ui.checkBoxSelfContacts->isChecked() )
{
return new QgsGeometrySelfContactCheck( featurePool );
}
else
{
return 0;
}
}

REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck> )

///////////////////////////////////////////////////////////////////////////////

template<> void QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const
{
ui.checkBoxSelfIntersections->setChecked( QgsSettings().value( sSettingsGroup + "checkSelfIntersections" ).toBool() );
@@ -41,9 +41,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-301</y>
<width>670</width>
<height>776</height>
<y>0</y>
<width>626</width>
<height>726</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_4">
@@ -117,10 +117,7 @@
<property name="flat">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>2</number>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<property name="leftMargin">
<number>2</number>
</property>
@@ -133,7 +130,7 @@
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxSelfIntersections">
<property name="text">
<string>Self intersections</string>
@@ -143,7 +140,7 @@
</property>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxDuplicateNodes">
<property name="text">
<string>Duplicate nodes</string>
@@ -153,7 +150,7 @@
</property>
</widget>
</item>
<item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxDegeneratePolygon">
<property name="text">
<string>Polygon with less than 3 nodes</string>
@@ -163,6 +160,16 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxSelfContacts">
<property name="text">
<string>Self contacts</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -728,7 +735,6 @@
<tabstop>checkBoxInputSelectedOnly</tabstop>
<tabstop>checkBoxSelfIntersections</tabstop>
<tabstop>checkBoxDuplicateNodes</tabstop>
<tabstop>checkBoxDegeneratePolygon</tabstop>
<tabstop>checkBoxPoint</tabstop>
<tabstop>checkBoxMultipoint</tabstop>
<tabstop>checkBoxLine</tabstop>

0 comments on commit 33d6b51

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