mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-23 00:05:43 -04:00
[Geometry checker] Store error values in map units, perform topology checks in map CRS
This commit is contained in:
parent
46e3ef7395
commit
23affe4fae
@ -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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() )
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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() )
|
||||
|
@ -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><i>Note: Topology checks are performed in the current map CRS.</i></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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user