1
0
mirror of https://github.com/qgis/QGIS.git synced 2025-03-25 00:05:03 -04:00

[Geometry checker] Store error values in map units, perform topology checks in map CRS

This commit is contained in:
Sandro Mani 2017-04-04 16:48:34 +02:00
parent 46e3ef7395
commit 23affe4fae
10 changed files with 49 additions and 96 deletions

@ -24,6 +24,7 @@ void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors
for ( const QString &layerId : featureIds.keys() ) for ( const QString &layerId : featureIds.keys() )
{ {
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];
double mapToLayerUnits = featurePool->getMapToLayerUnits();
if ( !getCompatibility( featurePool->getLayer()->geometryType() ) ) if ( !getCompatibility( featurePool->getLayer()->geometryType() ) )
{ {
continue; continue;
@ -47,7 +48,7 @@ void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors
double value; double value;
if ( checkThreshold( featurePool->getMapToLayerUnits(), multiGeom->geometryN( i ), value ) ) if ( checkThreshold( featurePool->getMapToLayerUnits(), multiGeom->geometryN( i ), value ) )
{ {
errors.append( new QgsGeometryCheckError( this, layerId, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) ); errors.append( new QgsGeometryCheckError( this, layerId, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value / ( mapToLayerUnits * mapToLayerUnits ), QgsGeometryCheckError::ValueArea ) );
} }
} }
} }
@ -56,7 +57,7 @@ void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors
double value; double value;
if ( checkThreshold( featurePool->getMapToLayerUnits(), geom, value ) ) if ( checkThreshold( featurePool->getMapToLayerUnits(), geom, value ) )
{ {
errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) ); errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->centroid(), QgsVertexId( 0 ), value / ( mapToLayerUnits * mapToLayerUnits ), QgsGeometryCheckError::ValueArea ) );
} }
} }
} }

@ -19,13 +19,14 @@
#include "qgsgeometrycheck.h" #include "qgsgeometrycheck.h"
#include "../utils/qgsfeaturepool.h" #include "../utils/qgsfeaturepool.h"
QgsGeometryCheckerContext::QgsGeometryCheckerContext( int _precision, const QString &_mapCrs, const QMap<QString, QgsFeaturePool *> &_featurePools )
QgsGeometryCheckerContext::QgsGeometryCheckerContext( int _precision, const QString &_crs, const QMap<QString, QgsFeaturePool *> &_featurePools )
: tolerance( qPow( 10, -_precision ) ) : tolerance( qPow( 10, -_precision ) )
, reducedTolerance( qPow( 10, -_precision / 2 ) ) , reducedTolerance( qPow( 10, -_precision / 2 ) )
, crs( _crs ) , mapCrs( _mapCrs )
, featurePools( _featurePools ) , featurePools( _featurePools )
{} {
}
QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, const QString &layerId, QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, const QString &layerId,
QgsFeatureId featureId, QgsFeatureId featureId,
@ -62,7 +63,7 @@ QgsRectangle QgsGeometryCheckError::affectedAreaBBox() const
return QgsRectangle(); return QgsRectangle();
} }
QString srcCrs = mCheck->getContext()->featurePools[ layerId() ]->getLayer()->crs().authid(); QString srcCrs = mCheck->getContext()->featurePools[ layerId() ]->getLayer()->crs().authid();
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( srcCrs, mCheck->getContext()->crs ); QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( srcCrs, mCheck->getContext()->mapCrs );
QgsRectangle rect = t.transformBoundingBox( geom->boundingBox() ); QgsRectangle rect = t.transformBoundingBox( geom->boundingBox() );
delete geom; delete geom;
return rect; return rect;

@ -32,10 +32,10 @@ class QgsFeaturePool;
struct QgsGeometryCheckerContext struct QgsGeometryCheckerContext
{ {
QgsGeometryCheckerContext( int _precision, const QString &_crs, const QMap<QString, QgsFeaturePool *> &_featurePools ); QgsGeometryCheckerContext( int _precision, const QString &_mapCrs, const QMap<QString, QgsFeaturePool *> &_featurePools );
const double tolerance; const double tolerance;
const double reducedTolerance; const double reducedTolerance;
const QString crs; const QString mapCrs;
const QMap<QString, QgsFeaturePool *> featurePools; const QMap<QString, QgsFeaturePool *> featurePools;
}; };
@ -113,9 +113,11 @@ class QgsGeometryCheckError
const QString &layerId() const { return mLayerId; } const QString &layerId() const { return mLayerId; }
QgsFeatureId featureId() const { return mFeatureId; } QgsFeatureId featureId() const { return mFeatureId; }
virtual QgsAbstractGeometry *geometry() const; virtual QgsAbstractGeometry *geometry() const;
// In map units
virtual QgsRectangle affectedAreaBBox() const; virtual QgsRectangle affectedAreaBBox() const;
virtual QString description() const { return mCheck->errorDescription(); } virtual QString description() const { return mCheck->errorDescription(); }
const QgsPoint &location() const { return mErrorLocation; } const QgsPoint &location() const { return mErrorLocation; }
// Lengths, areas in map units
QVariant value() const { return mValue; } QVariant value() const { return mValue; }
ValueType valueType() const { return mValueType; } ValueType valueType() const { return mValueType; }
QgsVertexId vidx() const { return mVidx; } QgsVertexId vidx() const { return mVidx; }

@ -29,7 +29,7 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
for ( const QString &layerId : featureIds.keys() ) for ( const QString &layerId : featureIds.keys() )
{ {
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( featurePool->getLayer()->crs().authid(), mContext->crs ); QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( featurePool->getLayer()->crs().authid(), mContext->mapCrs );
if ( !getCompatibility( featurePool->getLayer()->geometryType() ) ) if ( !getCompatibility( featurePool->getLayer()->geometryType() ) )
{ {
continue; continue;

@ -56,7 +56,7 @@ void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError *
double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) );
if ( dist < minLength ) if ( dist < minLength )
{ {
errors.append( new QgsGeometryCheckError( this, layerId, featureid, QgsPoint( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) ); errors.append( new QgsGeometryCheckError( this, layerId, featureid, QgsPoint( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist / mapToLayerUnits, QgsGeometryCheckError::ValueLength ) );
} }
} }
} }

@ -113,7 +113,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
{ {
const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &layerChanges = changes[layerId]; const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &layerChanges = changes[layerId];
QgsFeaturePool *featurePool = mContext->featurePools[layerId]; QgsFeaturePool *featurePool = mContext->featurePools[layerId];
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( featurePool->getLayer()->crs().authid(), mContext->crs ); QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( featurePool->getLayer()->crs().authid(), mContext->mapCrs );
for ( QgsFeatureId id : layerChanges.keys() ) for ( QgsFeatureId id : layerChanges.keys() )
{ {
bool removed = false; bool removed = false;
@ -152,7 +152,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
for ( const QString &layerId : mContext->featurePools.keys() ) for ( const QString &layerId : mContext->featurePools.keys() )
{ {
QgsFeaturePool *featurePool = mContext->featurePools[layerId]; QgsFeaturePool *featurePool = mContext->featurePools[layerId];
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->crs, featurePool->getLayer()->crs().authid() ); QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
recheckAreaFeatures[layerId] = featurePool->getIntersects( t.transform( recheckArea ) ); recheckAreaFeatures[layerId] = featurePool->getIntersects( t.transform( recheckArea ) );
// If only selected features were checked, confine the recheck areas to the selected features // If only selected features were checked, confine the recheck areas to the selected features
if ( featurePool->getSelectedOnly() ) if ( featurePool->getSelectedOnly() )

@ -67,20 +67,6 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome
{ {
int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) );
QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec );
double layerToMap = 1. / mChecker->getContext()->featurePools[error->layerId()]->getMapToLayerUnits();
QVariant value;
if ( error->valueType() == QgsGeometryCheckError::ValueLength )
{
value = QVariant::fromValue( error->value().toDouble() * layerToMap );
}
else if ( error->valueType() == QgsGeometryCheckError::ValueArea )
{
value = QVariant::fromValue( error->value().toDouble() * layerToMap * layerToMap );
}
else
{
value = error->value();
}
int row = table->rowCount(); int row = table->rowCount();
table->insertRow( row ); table->insertRow( row );
@ -91,7 +77,7 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome
table->setItem( row, 1, new QTableWidgetItem( error->description() ) ); table->setItem( row, 1, new QTableWidgetItem( error->description() ) );
table->setItem( row, 2, new QTableWidgetItem( posStr ) ); table->setItem( row, 2, new QTableWidgetItem( posStr ) );
QTableWidgetItem *valueItem = new QTableWidgetItem(); QTableWidgetItem *valueItem = new QTableWidgetItem();
valueItem->setData( Qt::EditRole, value ); valueItem->setData( Qt::EditRole, error->value() );
table->setItem( row, 3, valueItem ); table->setItem( row, 3, valueItem );
} }

@ -137,20 +137,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error )
int row = ui.tableWidgetErrors->rowCount(); int row = ui.tableWidgetErrors->rowCount();
int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) );
QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec );
double layerToMap = 1. / mChecker->getContext()->featurePools[error->layerId()]->getMapToLayerUnits();
QVariant value;
if ( error->valueType() == QgsGeometryCheckError::ValueLength )
{
value = QVariant::fromValue( error->value().toDouble() * layerToMap );
}
else if ( error->valueType() == QgsGeometryCheckError::ValueArea )
{
value = QVariant::fromValue( error->value().toDouble() * layerToMap * layerToMap );
}
else
{
value = error->value();
}
ui.tableWidgetErrors->insertRow( row ); ui.tableWidgetErrors->insertRow( row );
QTableWidgetItem *idItem = new QTableWidgetItem(); QTableWidgetItem *idItem = new QTableWidgetItem();
idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() ); idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() );
@ -158,7 +145,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error )
ui.tableWidgetErrors->setItem( row, 1, new QTableWidgetItem( error->description() ) ); ui.tableWidgetErrors->setItem( row, 1, new QTableWidgetItem( error->description() ) );
ui.tableWidgetErrors->setItem( row, 2, new QTableWidgetItem( posStr ) ); ui.tableWidgetErrors->setItem( row, 2, new QTableWidgetItem( posStr ) );
QTableWidgetItem *valueItem = new QTableWidgetItem(); QTableWidgetItem *valueItem = new QTableWidgetItem();
valueItem->setData( Qt::EditRole, value ); valueItem->setData( Qt::EditRole, error->value() );
ui.tableWidgetErrors->setItem( row, 3, valueItem ); ui.tableWidgetErrors->setItem( row, 3, valueItem );
ui.tableWidgetErrors->setItem( row, 4, new QTableWidgetItem( QLatin1String( "" ) ) ); ui.tableWidgetErrors->setItem( row, 4, new QTableWidgetItem( QLatin1String( "" ) ) );
ui.tableWidgetErrors->item( row, 0 )->setData( Qt::UserRole, QVariant::fromValue( error ) ); ui.tableWidgetErrors->item( row, 0 )->setData( Qt::UserRole, QVariant::fromValue( error ) );
@ -185,22 +172,9 @@ void QgsGeometryCheckerResultTab::updateError( QgsGeometryCheckError *error, boo
int row = mErrorMap.value( error ).row(); int row = mErrorMap.value( error ).row();
int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) );
QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec );
double layerToMap = 1. / mChecker->getContext()->featurePools[error->layerId()]->getMapToLayerUnits();
QVariant value;
if ( error->valueType() == QgsGeometryCheckError::ValueLength )
{
value = QVariant::fromValue( error->value().toDouble() * layerToMap );
}
else if ( error->valueType() == QgsGeometryCheckError::ValueArea )
{
value = QVariant::fromValue( error->value().toDouble() * layerToMap * layerToMap );
}
else
{
value = error->value();
}
ui.tableWidgetErrors->item( row, 2 )->setText( posStr ); ui.tableWidgetErrors->item( row, 2 )->setText( posStr );
ui.tableWidgetErrors->item( row, 3 )->setData( Qt::EditRole, value ); ui.tableWidgetErrors->item( row, 3 )->setData( Qt::EditRole, error->value() );
if ( error->status() == QgsGeometryCheckError::StatusFixed ) if ( error->status() == QgsGeometryCheckError::StatusFixed )
{ {
setRowStatus( row, Qt::green, tr( "Fixed: %1" ).arg( error->resolutionMessage() ), true ); setRowStatus( row, Qt::green, tr( "Fixed: %1" ).arg( error->resolutionMessage() ), true );

@ -183,14 +183,6 @@ void QgsGeometryCheckerSetupTab::validateInput()
nApplicable += factory->checkApplicability( ui, nPoint, nLineString, nPolygon ); nApplicable += factory->checkApplicability( ui, nPoint, nLineString, nPolygon );
} }
} }
QString prevCrs = ui.comboBoxTopologyCrs->currentText();
ui.comboBoxTopologyCrs->clear();
ui.comboBoxTopologyCrs->addItems( layerCrs );
ui.comboBoxTopologyCrs->setCurrentIndex( ui.comboBoxTopologyCrs->findText( prevCrs ) );
if ( ui.comboBoxTopologyCrs->currentIndex() == -1 )
{
ui.comboBoxTopologyCrs->setCurrentIndex( 0 );
}
bool outputOk = ui.radioButtonOutputModifyInput->isChecked() || !ui.lineEditOutputDirectory->text().isEmpty(); bool outputOk = ui.radioButtonOutputModifyInput->isChecked() || !ui.lineEditOutputDirectory->text().isEmpty();
mRunButton->setEnabled( !layers.isEmpty() && nApplicable > 0 && outputOk ); mRunButton->setEnabled( !layers.isEmpty() && nApplicable > 0 && outputOk );
@ -408,7 +400,7 @@ void QgsGeometryCheckerSetupTab::runChecks()
featurePools.insert( layer->id(), new QgsFeaturePool( layer, mapToLayerUnits, selectedOnly ) ); featurePools.insert( layer->id(), new QgsFeaturePool( layer, mapToLayerUnits, selectedOnly ) );
} }
QgsGeometryCheckerContext *context = new QgsGeometryCheckerContext( ui.spinBoxTolerance->value(), ui.comboBoxTopologyCrs->currentText(), featurePools ); QgsGeometryCheckerContext *context = new QgsGeometryCheckerContext( ui.spinBoxTolerance->value(), mIface->mapCanvas()->mapSettings().destinationCrs().authid(), featurePools );
QList<QgsGeometryCheck *> checks; QList<QgsGeometryCheck *> checks;
for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() )

@ -41,9 +41,9 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>-106</y> <y>0</y>
<width>626</width> <width>626</width>
<height>832</height> <height>820</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
@ -478,6 +478,13 @@
<property name="spacing"> <property name="spacing">
<number>2</number> <number>2</number>
</property> </property>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxDuplicates">
<property name="text">
<string>Check for duplicates</string>
</property>
</widget>
</item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxOverlapArea"> <widget class="QDoubleSpinBox" name="doubleSpinBoxOverlapArea">
<property name="decimals"> <property name="decimals">
@ -488,6 +495,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxGapArea">
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="checkBoxOverlaps"> <widget class="QCheckBox" name="checkBoxOverlaps">
<property name="sizePolicy"> <property name="sizePolicy">
@ -514,23 +531,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxGapArea">
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxDuplicates">
<property name="text">
<string>Check for duplicates</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="checkBoxCovered"> <widget class="QCheckBox" name="checkBoxCovered">
<property name="text"> <property name="text">
@ -538,6 +538,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" 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>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -585,16 +592,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="labelTopologyCrs">
<property name="text">
<string>CRS for topology checks:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxTopologyCrs"/>
</item>
</layout> </layout>
</item> </item>
<item> <item>