Skip to content

Commit

Permalink
[Geometry checker] Add point in polygon check
Browse files Browse the repository at this point in the history
  • Loading branch information
manisandro committed Oct 23, 2017
1 parent 3842f42 commit e88f32a
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/plugins/geometry_checker/CMakeLists.txt
Expand Up @@ -20,6 +20,7 @@ SET (geometrychecker_SRCS
checks/qgsgeometrycontainedcheck.cpp
checks/qgsgeometryoverlapcheck.cpp
checks/qgsgeometrypointcoveredbylinecheck.cpp
checks/qgsgeometrypointinpolygoncheck.cpp
checks/qgsgeometrysegmentlengthcheck.cpp
checks/qgsgeometryselfcontactcheck.cpp
checks/qgsgeometryselfintersectioncheck.cpp
Expand Down Expand Up @@ -59,6 +60,7 @@ SET (geometrychecker_MOC_HDRS
checks/qgsgeometrycontainedcheck.h
checks/qgsgeometryoverlapcheck.h
checks/qgsgeometrypointcoveredbylinecheck.h
checks/qgsgeometrypointinpolygoncheck.h
checks/qgsgeometrysegmentlengthcheck.h
checks/qgsgeometryselfcontactcheck.h
checks/qgsgeometryselfintersectioncheck.h
Expand Down
110 changes: 110 additions & 0 deletions src/plugins/geometry_checker/checks/qgsgeometrypointinpolygoncheck.cpp
@@ -0,0 +1,110 @@
/***************************************************************************
qgsgeometrypointinpolygoncheck.cpp
---------------------
begin : June 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 "qgsgeometrypointinpolygoncheck.h"
#include "qgspolygon.h"
#include "qgsgeometryengine.h"

void QgsGeometryPointInPolygonCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, true );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
const QgsAbstractGeometry *geom = layerFeature.geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
const QgsPoint *point = dynamic_cast<const QgsPoint *>( QgsGeometryCheckerUtils::getGeomPart( geom, iPart ) );
if ( !point )
{
// Should not happen
continue;
}

// Check whether point is contained by a fully contained by a polygon
bool contained = false;
QgsRectangle rect( point->x() - mContext->tolerance, point->y() - mContext->tolerance,
point->x() + mContext->tolerance, point->y() + mContext->tolerance );
QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, featureIds.keys(), rect, {QgsWkbTypes::PolygonGeometry} );
for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures )
{
const QgsAbstractGeometry *testGeom = checkFeature.geometry();
for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart )
{
const QgsPolygonV2 *testPoly = dynamic_cast<const QgsPolygonV2 *>( QgsGeometryCheckerUtils::getGeomPart( testGeom, jPart ) );
if ( !testPoly )
{
continue;
}
QSharedPointer<QgsGeometryEngine> testGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( testPoly, mContext->tolerance );
if ( testGeomEngine->contains( *point ) )
{
// Check whether point does not lie on a ring boundary
bool touchesBoundary = false;
if ( dynamic_cast<const QgsLineString *>( testPoly->exteriorRing() ) &&
QgsGeometryCheckerUtils::pointOnLine( *point, static_cast<const QgsLineString *>( testPoly->exteriorRing() ), mContext->tolerance ) )
{
touchesBoundary = true;
}
else
{
for ( int jRing = 1, mRings = testPoly->ringCount( jPart ); jRing < mRings; ++jRing )
{
if ( dynamic_cast<const QgsLineString *>( testPoly->interiorRing( jRing - 1 ) ) &&
QgsGeometryCheckerUtils::pointOnLine( *point, static_cast<const QgsLineString *>( testPoly->interiorRing( jRing - 1 ) ), mContext->tolerance ) )
{
touchesBoundary = true;
break;
}
}
}
if ( !touchesBoundary )
{
// Ok, point is contained by a polygon and does not touch its boundaries
contained = true;
break;
}
}
}
if ( contained )
{
break;
}
}
if ( !contained )
{
errors.append( new QgsGeometryCheckError( this, layerFeature, *point, QgsVertexId( iPart, 0, 0 ) ) );
}
}
}
}

void QgsGeometryPointInPolygonCheck::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 QgsGeometryPointInPolygonCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "No action" );
return methods;
}
@@ -0,0 +1,38 @@
/***************************************************************************
qgsgeometrypointinpolygoncheck.h
---------------------
begin : June 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 QGSGEOMETRYPOINTINPOLYGONCHECK_H
#define QGSGEOMETRYPOINTINPOLYGONCHECK_H

#include "qgsgeometrycheck.h"

class QgsGeometryPointInPolygonCheck : public QgsGeometryCheck
{
Q_OBJECT

public:
QgsGeometryPointInPolygonCheck( QgsGeometryCheckerContext *context )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PointGeometry}, context )
{}
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( "Point not in polygon" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryPointInPolygonCheck" ); }
private:
enum ResolutionMethod { NoChange };
};

#endif // QGSGEOMETRYPOINTINPOLYGONCHECK_H
29 changes: 29 additions & 0 deletions src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp
Expand Up @@ -29,6 +29,7 @@
#include "checks/qgsgeometrymultipartcheck.h"
#include "checks/qgsgeometryoverlapcheck.h"
#include "checks/qgsgeometrypointcoveredbylinecheck.h"
#include "checks/qgsgeometrypointinpolygoncheck.h"
#include "checks/qgsgeometrysegmentlengthcheck.h"
#include "checks/qgsgeometryselfcontactcheck.h"
#include "checks/qgsgeometryselfintersectioncheck.h"
Expand Down Expand Up @@ -415,6 +416,34 @@ REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT<QgsGeometryPointCo

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

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

template<> bool QgsGeometryCheckFactoryT<QgsGeometryPointInPolygonCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int nPoint, int /*nLineString*/, int /*nPolygon*/ ) const
{
ui.checkPointInPolygon->setEnabled( nPoint > 0 );
return ui.checkPointInPolygon->isEnabled();
}

template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryPointInPolygonCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
{
QgsSettings().setValue( sSettingsGroup + "checkPointInPolygon", ui.checkPointInPolygon->isChecked() );
if ( ui.checkPointInPolygon->isEnabled() && ui.checkPointInPolygon->isChecked() )
{
return new QgsGeometryPointInPolygonCheck( context );
}
else
{
return nullptr;
}
}

REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT<QgsGeometryPointInPolygonCheck> )

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

template<> void QgsGeometryCheckFactoryT<QgsGeometrySegmentLengthCheck>::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const
{
ui.checkBoxSegmentLength->setChecked( QgsSettings().value( sSettingsGroup + "checkSegmentLength" ).toBool() );
Expand Down
13 changes: 10 additions & 3 deletions src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui
Expand Up @@ -43,7 +43,7 @@
<x>0</x>
<y>0</y>
<width>626</width>
<height>918</height>
<height>941</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_4">
Expand Down Expand Up @@ -511,7 +511,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QCheckBox" name="checkLineIntersection">
<property name="text">
<string>Lines must not intersect</string>
Expand Down Expand Up @@ -542,7 +542,7 @@
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<item row="9" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;i&gt;Note: Topology checks are performed in the current map CRS.&lt;/i&gt;</string>
Expand All @@ -566,6 +566,13 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="checkPointInPolygon">
<property name="text">
<string>Points must properly lie inside a polygon</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down

0 comments on commit e88f32a

Please sign in to comment.