Make QgsGeometry independent from QgsProject + better avoid intersections API

This commit is contained in:
Martin Dobias 2017-01-06 17:24:32 +08:00
parent 8c340f7144
commit 0513bb357c
15 changed files with 72 additions and 62 deletions

View File

@ -966,6 +966,7 @@ method to determine if a geometry is valid.
- static bool compare( const QgsPolygon& p1, const QgsPolygon& p2, double epsilon ) has been renamed to comparePolygons
- static bool compare( const QgsMultiPolygon& p1, const QgsMultiPolygon& p2, double epsilon ) has been renamed to compareMultiPolygons
- smoothLine and smoothPolygon are no longer public API (use smooth() instead)
- avoidIntersections() got an extra argument: list of layers to include in the operation (previously read from active QgsProject)
QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer}

View File

@ -741,10 +741,11 @@ class QgsGeometry
* 1 if geometry is not of polygon type,
* 2 if avoid intersection would change the geometry type,
* 3 other error during intersection removal
* @param avoidIntersectionsLayers list of layers to check for intersections
* @param ignoreFeatures possibility to give a list of features where intersections should be ignored (not available in python bindings)
* @note added in 1.5
*/
int avoidIntersections();
int avoidIntersections( const QList<QgsVectorLayer*>& avoidIntersectionsLayers );
class Error
{

View File

@ -396,14 +396,14 @@ class QgsProject : QObject, QgsExpressionContextGenerator
*
* @note Added in QGIS 3.0
*/
QStringList avoidIntersectionsList() const;
QList<QgsVectorLayer*> avoidIntersectionsLayers() const;
/**
* A list of layers with which intersections should be avoided.
*
* @note Added in QGIS 3.0
*/
void setAvoidIntersectionsList( const QStringList& avoidIntersectionsList );
void setAvoidIntersectionsLayers( const QList<QgsVectorLayer*>& layers );
QVariantMap customVariables() const;
void setCustomVariables( const QVariantMap& customVariables );
int count() const;
@ -646,11 +646,11 @@ class QgsProject : QObject, QgsExpressionContextGenerator
void topologicalEditingChanged();
/**
* Emitted whenever avoidIntersectionsList has changed.
* Emitted whenever avoidIntersectionsLayers has changed.
*
* @note Added in QGIS 3.0
*/
void avoidIntersectionsListChanged();
void avoidIntersectionsLayersChanged();
/**
* Emitted when the map theme collection changes.

View File

@ -909,7 +909,7 @@ void QgsGPSInformationWidget::on_mBtnCloseFeature_clicked()
f->setGeometry( g );
QgsGeometry featGeom = f->geometry();
int avoidIntersectionsReturn = featGeom.avoidIntersections();
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
f->setGeometry( featGeom );
if ( avoidIntersectionsReturn == 1 )
{

View File

@ -7656,7 +7656,7 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
geom = newGeometry;
}
// avoid intersection if enabled in digitize settings
geom.avoidIntersections();
geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
}
// now create new feature using pasted feature as a template. This automatically handles default

View File

@ -273,7 +273,7 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e )
delete g;
QgsGeometry featGeom = f->geometry();
int avoidIntersectionsReturn = featGeom.avoidIntersections();
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
f->setGeometry( featGeom );
if ( avoidIntersectionsReturn == 1 )
{
@ -295,17 +295,14 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e )
//use always topological editing for avoidIntersection.
//Otherwise, no way to guarantee the geometries don't have a small gap in between.
QStringList intersectionLayers = QgsProject::instance()->avoidIntersectionsList();
QList<QgsVectorLayer*> intersectionLayers = QgsProject::instance()->avoidIntersectionsLayers();
bool avoidIntersection = !intersectionLayers.isEmpty();
if ( avoidIntersection ) //try to add topological points also to background layers
{
QStringList::const_iterator lIt = intersectionLayers.constBegin();
for ( ; lIt != intersectionLayers.constEnd(); ++lIt )
Q_FOREACH ( QgsVectorLayer* vl, intersectionLayers )
{
QgsMapLayer* ml = QgsProject::instance()->mapLayer( *lIt );
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml );
//can only add topological points if background layer is editable...
if ( vl && vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() )
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() )
{
vl->addTopologicalPoints( f->geometry() );
}

View File

@ -149,7 +149,7 @@ void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
QgsCurvePolygon* cp = new QgsCurvePolygon();
cp->setExteriorRing( curveToAdd );
QgsGeometry* geom = new QgsGeometry( cp );
geom->avoidIntersections();
geom->avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
const QgsCurvePolygon* cpGeom = dynamic_cast<const QgsCurvePolygon*>( geom->geometry() );
if ( !cpGeom )

View File

@ -17,6 +17,7 @@
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgisapp.h"
@ -108,7 +109,7 @@ void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
QHash<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures;
ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() );
if ( geom.avoidIntersections( ignoreFeatures ) != 0 )
if ( geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers(), ignoreFeatures ) != 0 )
{
emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL );
vlayer->destroyEditCommand();

View File

@ -144,7 +144,7 @@ QgsSnappingLayerTreeModel::QgsSnappingLayerTreeModel( QgsProject* project, QObje
, mLayerTreeModel( nullptr )
{
connect( project, &QgsProject::snappingConfigChanged, this, &QgsSnappingLayerTreeModel::onSnappingSettingsChanged );
connect( project, &QgsProject::avoidIntersectionsListChanged, this, &QgsSnappingLayerTreeModel::onSnappingSettingsChanged );
connect( project, &QgsProject::avoidIntersectionsLayersChanged, this, &QgsSnappingLayerTreeModel::onSnappingSettingsChanged );
}
QgsSnappingLayerTreeModel::~QgsSnappingLayerTreeModel()
@ -494,7 +494,7 @@ QVariant QgsSnappingLayerTreeModel::data( const QModelIndex& idx, int role ) con
{
if ( role == Qt::CheckStateRole && vl->geometryType() == QgsWkbTypes::PolygonGeometry )
{
if ( mProject->avoidIntersectionsList().contains( vl->id() ) )
if ( mProject->avoidIntersectionsLayers().contains( vl ) )
{
return Qt::Checked;
}
@ -620,14 +620,14 @@ bool QgsSnappingLayerTreeModel::setData( const QModelIndex& index, const QVarian
if ( !mIndividualLayerSettings.contains( vl ) )
return false;
QStringList avoidIntersectionsList = mProject->avoidIntersectionsList();
QList<QgsVectorLayer*> avoidIntersectionsList = mProject->avoidIntersectionsLayers();
if ( value.toInt() == Qt::Checked && !avoidIntersectionsList.contains( vl->id() ) )
avoidIntersectionsList.append( vl->id() );
if ( value.toInt() == Qt::Checked && !avoidIntersectionsList.contains( vl ) )
avoidIntersectionsList.append( vl );
else
avoidIntersectionsList.removeAll( vl->id() );
avoidIntersectionsList.removeAll( vl );
mProject->setAvoidIntersectionsList( avoidIntersectionsList );
mProject->setAvoidIntersectionsLayers( avoidIntersectionsList );
return true;
}
}

View File

@ -1871,14 +1871,14 @@ bool QgsGeometry::deletePart( int partNum )
return ok;
}
int QgsGeometry::avoidIntersections( const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
int QgsGeometry::avoidIntersections( const QList<QgsVectorLayer*>& avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
{
if ( !d->geometry )
{
return 1;
}
QgsAbstractGeometry* diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), ignoreFeatures );
QgsAbstractGeometry* diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, ignoreFeatures );
if ( diffGeom )
{
detach( false );

View File

@ -795,10 +795,12 @@ class CORE_EXPORT QgsGeometry
* 1 if geometry is not of polygon type,
* 2 if avoid intersection would change the geometry type,
* 3 other error during intersection removal
* @param avoidIntersectionsLayers list of layers to check for intersections
* @param ignoreFeatures possibility to give a list of features where intersections should be ignored (not available in python bindings)
* @note added in 1.5
*/
int avoidIntersections( const QHash<QgsVectorLayer*, QSet<QgsFeatureId> >& ignoreFeatures = ( QHash<QgsVectorLayer*, QSet<QgsFeatureId> >() ) );
int avoidIntersections( const QList<QgsVectorLayer*>& avoidIntersectionsLayers,
const QHash<QgsVectorLayer*, QSet<QgsFeatureId> >& ignoreFeatures = ( QHash<QgsVectorLayer*, QSet<QgsFeatureId> >() ) );
/** \ingroup core
*/

View File

@ -224,7 +224,9 @@ bool QgsGeometryEditUtils::deletePart( QgsAbstractGeometry* geom, int partNum )
return c->removeGeometry( partNum );
}
QgsAbstractGeometry* QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry& geom, QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures )
QgsAbstractGeometry* QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry& geom,
const QList<QgsVectorLayer*>& avoidIntersectionsLayers,
QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures )
{
QScopedPointer<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) );
if ( geomEngine.isNull() )
@ -240,39 +242,32 @@ QgsAbstractGeometry* QgsGeometryEditUtils::avoidIntersections( const QgsAbstract
return nullptr;
}
QStringList avoidIntersectionsList = QgsProject::instance()->avoidIntersectionsList();
if ( avoidIntersectionsList.isEmpty() )
if ( avoidIntersectionsLayers.isEmpty() )
return nullptr; //no intersections stored in project does not mean error
QList< QgsAbstractGeometry* > nearGeometries;
//go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
QgsVectorLayer* currentLayer = nullptr;
QStringList::const_iterator aIt = avoidIntersectionsList.constBegin();
for ( ; aIt != avoidIntersectionsList.constEnd(); ++aIt )
Q_FOREACH ( QgsVectorLayer* currentLayer, avoidIntersectionsLayers )
{
currentLayer = dynamic_cast<QgsVectorLayer*>( QgsProject::instance()->mapLayer( *aIt ) );
if ( currentLayer )
QgsFeatureIds ignoreIds;
QHash<QgsVectorLayer*, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.find( currentLayer );
if ( ignoreIt != ignoreFeatures.constEnd() )
ignoreIds = ignoreIt.value();
QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
.setFlags( QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeature f;
while ( fi.nextFeature( f ) )
{
QgsFeatureIds ignoreIds;
QHash<QgsVectorLayer*, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.find( currentLayer );
if ( ignoreIt != ignoreFeatures.constEnd() )
ignoreIds = ignoreIt.value();
if ( ignoreIds.contains( f.id() ) )
continue;
QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
.setFlags( QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeature f;
while ( fi.nextFeature( f ) )
{
if ( ignoreIds.contains( f.id() ) )
continue;
if ( !f.hasGeometry() )
continue;
if ( !f.hasGeometry() )
continue;
nearGeometries << f.geometry().geometry()->clone();
}
nearGeometries << f.geometry().geometry()->clone();
}
}

View File

@ -59,9 +59,12 @@ class QgsGeometryEditUtils
/** Alters a geometry so that it avoids intersections with features from all open vector layers.
* @param geom geometry to alter
* @param avoidIntersectionsLayers list of layers to check for intersections
* @param ignoreFeatures map of layer to feature id of features to ignore
*/
static QgsAbstractGeometry* avoidIntersections( const QgsAbstractGeometry& geom, QHash<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures = ( QHash<QgsVectorLayer*, QSet<QgsFeatureId> >() ) );
static QgsAbstractGeometry* avoidIntersections( const QgsAbstractGeometry& geom,
const QList<QgsVectorLayer*>& avoidIntersectionsLayers,
QHash<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures = ( QHash<QgsVectorLayer*, QSet<QgsFeatureId> >() ) );
};
#endif // QGSGEOMETRYEDITUTILS_H

View File

@ -1023,15 +1023,25 @@ void QgsProject::setCustomVariables( const QVariantMap& variables )
emit customVariablesChanged();
}
QStringList QgsProject::avoidIntersectionsList() const
QList<QgsVectorLayer*> QgsProject::avoidIntersectionsLayers() const
{
return readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
QList<QgsVectorLayer*> layers;
QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
Q_FOREACH ( const QString& layerId, layerIds )
{
if ( QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mapLayer( layerId ) ) )
layers << vlayer;
}
return layers;
}
void QgsProject::setAvoidIntersectionsList( const QStringList& avoidIntersectionsList )
void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer*>& layers )
{
writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), avoidIntersectionsList );
emit avoidIntersectionsListChanged();
QStringList list;
Q_FOREACH ( QgsVectorLayer* layer, layers )
list << layer->id();
writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
emit avoidIntersectionsLayersChanged();
}
QgsExpressionContext QgsProject::createExpressionContext() const

View File

@ -78,7 +78,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
Q_PROPERTY( QgsCoordinateReferenceSystem crs READ crs WRITE setCrs )
Q_PROPERTY( QgsMapThemeCollection* mapThemeCollection READ mapThemeCollection NOTIFY mapThemeCollectionChanged )
Q_PROPERTY( QgsSnappingConfig snappingConfig READ snappingConfig WRITE setSnappingConfig NOTIFY snappingConfigChanged )
Q_PROPERTY( QStringList avoidIntersectionsList READ avoidIntersectionsList WRITE setAvoidIntersectionsList NOTIFY avoidIntersectionsListChanged )
Q_PROPERTY( QList<QgsVectorLayer*> avoidIntersectionsLayers READ avoidIntersectionsLayers WRITE setAvoidIntersectionsLayers NOTIFY avoidIntersectionsLayersChanged )
public:
//! Returns the QgsProject singleton instance
@ -465,14 +465,14 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
*
* @note Added in QGIS 3.0
*/
QStringList avoidIntersectionsList() const;
QList<QgsVectorLayer*> avoidIntersectionsLayers() const;
/**
* A list of layers with which intersections should be avoided.
*
* @note Added in QGIS 3.0
*/
void setAvoidIntersectionsList( const QStringList& avoidIntersectionsList );
void setAvoidIntersectionsLayers( const QList<QgsVectorLayer*>& layers );
/**
* A map of custom project variables.
@ -760,11 +760,11 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
void topologicalEditingChanged();
/**
* Emitted whenever avoidIntersectionsList has changed.
* Emitted whenever avoidIntersectionsLayers has changed.
*
* @note Added in QGIS 3.0
*/
void avoidIntersectionsListChanged();
void avoidIntersectionsLayersChanged();
/**
* Emitted when the map theme collection changes.