mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-30 00:04:11 -04:00
[Geometry checker] Add dangle check
This commit is contained in:
parent
84184d453f
commit
7fb1c5515b
@ -8,6 +8,7 @@ SET (geometrychecker_SRCS
|
|||||||
qgsgeometrycheckfactory.cpp
|
qgsgeometrycheckfactory.cpp
|
||||||
checks/qgsgeometryanglecheck.cpp
|
checks/qgsgeometryanglecheck.cpp
|
||||||
checks/qgsgeometryareacheck.cpp
|
checks/qgsgeometryareacheck.cpp
|
||||||
|
checks/qgsgeometrydanglecheck.cpp
|
||||||
checks/qgsgeometrydegeneratepolygoncheck.cpp
|
checks/qgsgeometrydegeneratepolygoncheck.cpp
|
||||||
checks/qgsgeometryduplicatecheck.cpp
|
checks/qgsgeometryduplicatecheck.cpp
|
||||||
checks/qgsgeometryduplicatenodescheck.cpp
|
checks/qgsgeometryduplicatenodescheck.cpp
|
||||||
@ -45,6 +46,7 @@ SET (geometrychecker_MOC_HDRS
|
|||||||
checks/qgsgeometryanglecheck.h
|
checks/qgsgeometryanglecheck.h
|
||||||
checks/qgsgeometryareacheck.h
|
checks/qgsgeometryareacheck.h
|
||||||
checks/qgsgeometrycheck.h
|
checks/qgsgeometrycheck.h
|
||||||
|
checks/qgsgeometrydanglecheck.h
|
||||||
checks/qgsgeometrydegeneratepolygoncheck.h
|
checks/qgsgeometrydegeneratepolygoncheck.h
|
||||||
checks/qgsgeometryduplicatecheck.h
|
checks/qgsgeometryduplicatecheck.h
|
||||||
checks/qgsgeometryduplicatenodescheck.h
|
checks/qgsgeometryduplicatenodescheck.h
|
||||||
|
109
src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.cpp
Normal file
109
src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsgeometrydanglecheck.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 "qgsgeometrydanglecheck.h"
|
||||||
|
#include "qgslinestring.h"
|
||||||
|
|
||||||
|
void QgsGeometryDangleCheck::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 );
|
||||||
|
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
|
||||||
|
{
|
||||||
|
const QgsAbstractGeometry *geom = layerFeature.geometry();
|
||||||
|
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
|
||||||
|
{
|
||||||
|
const QgsLineString *line = dynamic_cast<const QgsLineString *>( QgsGeometryCheckerUtils::getGeomPart( geom, iPart ) );
|
||||||
|
if ( !line )
|
||||||
|
{
|
||||||
|
// Should not happen
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Check that start and end node lie on a line
|
||||||
|
int nVerts = geom->vertexCount( iPart, 0 );
|
||||||
|
const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, 0, 0 ) );
|
||||||
|
const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, 0, nVerts - 1 ) );
|
||||||
|
|
||||||
|
bool p1touches = QgsGeometryCheckerUtils::pointOnLine( p1, line, mContext->tolerance, true );
|
||||||
|
bool p2touches = QgsGeometryCheckerUtils::pointOnLine( p2, line, mContext->tolerance, true );
|
||||||
|
|
||||||
|
if ( p1touches && p2touches )
|
||||||
|
{
|
||||||
|
// Both endpoints lie on line itself
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether endpoints line on another line in the layer
|
||||||
|
QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, QList<QString>() << layerFeature.layer().id(), line->boundingBox(), {QgsWkbTypes::LineGeometry} );
|
||||||
|
for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures )
|
||||||
|
{
|
||||||
|
if ( checkFeature.feature().id() == layerFeature.feature().id() )
|
||||||
|
{
|
||||||
|
// Skip current feature, it was already checked above
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QgsAbstractGeometry *testGeom = checkFeature.geometry();
|
||||||
|
for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart )
|
||||||
|
{
|
||||||
|
const QgsLineString *testLine = dynamic_cast<const QgsLineString *>( QgsGeometryCheckerUtils::getGeomPart( testGeom, jPart ) );
|
||||||
|
if ( !testLine )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p1touches = p1touches || QgsGeometryCheckerUtils::pointOnLine( p1, testLine, mContext->tolerance );
|
||||||
|
p2touches = p2touches || QgsGeometryCheckerUtils::pointOnLine( p2, testLine, mContext->tolerance );
|
||||||
|
if ( p1touches && p2touches )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( p1touches && p2touches )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( p1touches && p2touches )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !p1touches )
|
||||||
|
{
|
||||||
|
errors.append( new QgsGeometryCheckError( this, layerFeature, p1, QgsVertexId( iPart, 0, 0 ) ) );
|
||||||
|
}
|
||||||
|
if ( !p2touches )
|
||||||
|
{
|
||||||
|
errors.append( new QgsGeometryCheckError( this, layerFeature, p2, QgsVertexId( iPart, 0, nVerts - 1 ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGeometryDangleCheck::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 QgsGeometryDangleCheck::getResolutionMethods() const
|
||||||
|
{
|
||||||
|
static QStringList methods = QStringList() << tr( "No action" );
|
||||||
|
return methods;
|
||||||
|
}
|
38
src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.h
Normal file
38
src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsgeometrydanglecheck.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 QGSGEOMETRYDANGLECHECK_H
|
||||||
|
#define QGSGEOMETRYDANGLECHECK_H
|
||||||
|
|
||||||
|
#include "qgsgeometrycheck.h"
|
||||||
|
|
||||||
|
class QgsGeometryDangleCheck : public QgsGeometryCheck
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QgsGeometryDangleCheck( QgsGeometryCheckerContext *context )
|
||||||
|
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry}, 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( "Dangle" ); }
|
||||||
|
QString errorName() const override { return QStringLiteral( "QgsGeometryDangleCheck" ); }
|
||||||
|
private:
|
||||||
|
enum ResolutionMethod { NoChange };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSGEOMETRYDANGLECHECK_H
|
@ -19,6 +19,7 @@
|
|||||||
#include "checks/qgsgeometryanglecheck.h"
|
#include "checks/qgsgeometryanglecheck.h"
|
||||||
#include "checks/qgsgeometryareacheck.h"
|
#include "checks/qgsgeometryareacheck.h"
|
||||||
#include "checks/qgsgeometrycontainedcheck.h"
|
#include "checks/qgsgeometrycontainedcheck.h"
|
||||||
|
#include "checks/qgsgeometrydanglecheck.h"
|
||||||
#include "checks/qgsgeometrydegeneratepolygoncheck.h"
|
#include "checks/qgsgeometrydegeneratepolygoncheck.h"
|
||||||
#include "checks/qgsgeometryduplicatecheck.h"
|
#include "checks/qgsgeometryduplicatecheck.h"
|
||||||
#include "checks/qgsgeometryduplicatenodescheck.h"
|
#include "checks/qgsgeometryduplicatenodescheck.h"
|
||||||
@ -126,6 +127,34 @@ REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT<QgsGeometryContain
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template<> void QgsGeometryCheckFactoryT<QgsGeometryDangleCheck>::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const
|
||||||
|
{
|
||||||
|
ui.checkBoxDangle->setChecked( QgsSettings().value( sSettingsGroup + "checkDangle" ).toBool() );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDangleCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int /*nPolygon*/ ) const
|
||||||
|
{
|
||||||
|
ui.checkBoxDangle->setEnabled( nLineString > 0 );
|
||||||
|
return ui.checkBoxDangle->isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDangleCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
|
||||||
|
{
|
||||||
|
QgsSettings().setValue( sSettingsGroup + "checkDangle", ui.checkBoxDangle->isChecked() );
|
||||||
|
if ( ui.checkBoxDangle->isEnabled() && ui.checkBoxDangle->isChecked() )
|
||||||
|
{
|
||||||
|
return new QgsGeometryDangleCheck( context );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT<QgsGeometryDangleCheck> )
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<> void QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const
|
template<> void QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const
|
||||||
{
|
{
|
||||||
ui.checkBoxDegeneratePolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkDegeneratePolygon" ).toBool() );
|
ui.checkBoxDegeneratePolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkDegeneratePolygon" ).toBool() );
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>626</width>
|
<width>626</width>
|
||||||
<height>820</height>
|
<height>872</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
@ -280,6 +280,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBoxDangle">
|
||||||
|
<property name="text">
|
||||||
|
<string>Lines must not have dangles</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -206,6 +206,22 @@ namespace QgsGeometryCheckerUtils
|
|||||||
return nom / std::sqrt( dx * dx + dy * dy );
|
return nom / std::sqrt( dx * dx + dy * dy );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities )
|
||||||
|
{
|
||||||
|
int nVerts = line->vertexCount();
|
||||||
|
for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i )
|
||||||
|
{
|
||||||
|
QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) );
|
||||||
|
QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
|
||||||
|
double dist = pointLineDist( p1, p2, 1 );
|
||||||
|
if ( dist < tol )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol )
|
double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol )
|
||||||
{
|
{
|
||||||
double len = 0;
|
double len = 0;
|
||||||
|
@ -126,6 +126,8 @@ namespace QgsGeometryCheckerUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities = false );
|
||||||
|
|
||||||
double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol );
|
double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user