[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

View File

@ -24,6 +24,7 @@ void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors
for ( const QString &layerId : featureIds.keys() )
{
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];
double mapToLayerUnits = featurePool->getMapToLayerUnits();
if ( !getCompatibility( featurePool->getLayer()->geometryType() ) )
{
continue;
@ -47,7 +48,7 @@ void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors
double 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;
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 ) );
}
}
}

View File

@ -19,13 +19,14 @@
#include "qgsgeometrycheck.h"
#include "../utils/qgsfeaturepool.h"
QgsGeometryCheckerContext::QgsGeometryCheckerContext( int _precision, const QString &_crs, const QMap<QString, QgsFeaturePool *> &_featurePools )
QgsGeometryCheckerContext::QgsGeometryCheckerContext( int _precision, const QString &_mapCrs, const QMap<QString, QgsFeaturePool *> &_featurePools )
: tolerance( qPow( 10, -_precision ) )
, reducedTolerance( qPow( 10, -_precision / 2 ) )
, crs( _crs )
, mapCrs( _mapCrs )
, featurePools( _featurePools )
{}
{
}
QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, const QString &layerId,
QgsFeatureId featureId,
@ -62,7 +63,7 @@ QgsRectangle QgsGeometryCheckError::affectedAreaBBox() const
return QgsRectangle();
}
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() );
delete geom;
return rect;

View File

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

View File

@ -29,7 +29,7 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
for ( const QString &layerId : featureIds.keys() )
{
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() ) )
{
continue;

View File

@ -56,7 +56,7 @@ void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError *
double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) );
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 ) );
}
}
}

View File

@ -113,7 +113,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
{
const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &layerChanges = changes[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() )
{
bool removed = false;
@ -152,7 +152,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
for ( const QString &layerId : mContext->featurePools.keys() )
{
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 ) );
// If only selected features were checked, confine the recheck areas to the selected features
if ( featurePool->getSelectedOnly() )

View File

@ -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() ) ) ) );
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();
table->insertRow( row );
@ -91,7 +77,7 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome
table->setItem( row, 1, new QTableWidgetItem( error->description() ) );
table->setItem( row, 2, new QTableWidgetItem( posStr ) );
QTableWidgetItem *valueItem = new QTableWidgetItem();
valueItem->setData( Qt::EditRole, value );
valueItem->setData( Qt::EditRole, error->value() );
table->setItem( row, 3, valueItem );
}

View File

@ -137,20 +137,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error )
int row = ui.tableWidgetErrors->rowCount();
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 );
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 );
QTableWidgetItem *idItem = new QTableWidgetItem();
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, 2, new QTableWidgetItem( posStr ) );
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, 4, new QTableWidgetItem( QLatin1String( "" ) ) );
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 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 );
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, 3 )->setData( Qt::EditRole, value );
ui.tableWidgetErrors->item( row, 3 )->setData( Qt::EditRole, error->value() );
if ( error->status() == QgsGeometryCheckError::StatusFixed )
{
setRowStatus( row, Qt::green, tr( "Fixed: %1" ).arg( error->resolutionMessage() ), true );

View File

@ -183,14 +183,6 @@ void QgsGeometryCheckerSetupTab::validateInput()
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();
mRunButton->setEnabled( !layers.isEmpty() && nApplicable > 0 && outputOk );
@ -408,7 +400,7 @@ void QgsGeometryCheckerSetupTab::runChecks()
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;
for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() )

View File

@ -41,9 +41,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-106</y>
<y>0</y>
<width>626</width>
<height>832</height>
<height>820</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_4">
@ -478,6 +478,13 @@
<property name="spacing">
<number>2</number>
</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">
<widget class="QDoubleSpinBox" name="doubleSpinBoxOverlapArea">
<property name="decimals">
@ -488,6 +495,16 @@
</property>
</widget>
</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">
<widget class="QCheckBox" name="checkBoxOverlaps">
<property name="sizePolicy">
@ -514,23 +531,6 @@
</property>
</widget>
</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">
<widget class="QCheckBox" name="checkBoxCovered">
<property name="text">
@ -538,6 +538,13 @@
</property>
</widget>
</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>
</widget>
</item>
@ -585,16 +592,6 @@
</property>
</widget>
</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>
</item>
<item>