Skip to content

Commit

Permalink
Make QgsGeometry independent from QgsProject + better avoid intersect…
Browse files Browse the repository at this point in the history
…ions API
  • Loading branch information
wonder-sk committed Jan 7, 2017
1 parent 8c340f7 commit 0513bb3
Show file tree
Hide file tree
Showing 15 changed files with 72 additions and 62 deletions.
1 change: 1 addition & 0 deletions doc/api_break.dox
Expand Up @@ -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}
Expand Down
3 changes: 2 additions & 1 deletion python/core/geometry/qgsgeometry.sip
Expand Up @@ -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
{
Expand Down
8 changes: 4 additions & 4 deletions python/core/qgsproject.sip
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/app/gps/qgsgpsinformationwidget.cpp
Expand Up @@ -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 )
{
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgisapp.cpp
Expand Up @@ -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
Expand Down
11 changes: 4 additions & 7 deletions src/app/qgsmaptooladdfeature.cpp
Expand Up @@ -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 )
{
Expand All @@ -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() );
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsmaptooladdpart.cpp
Expand Up @@ -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 )
Expand Down
3 changes: 2 additions & 1 deletion src/app/qgsmaptoolreshape.cpp
Expand Up @@ -17,6 +17,7 @@
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgisapp.h"

Expand Down Expand Up @@ -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();
Expand Down
14 changes: 7 additions & 7 deletions src/app/qgssnappinglayertreemodel.cpp
Expand Up @@ -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()
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -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 );
Expand Down
4 changes: 3 additions & 1 deletion src/core/geometry/qgsgeometry.h
Expand Up @@ -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
*/
Expand Down
45 changes: 20 additions & 25 deletions src/core/geometry/qgsgeometryeditutils.cpp
Expand Up @@ -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() )
Expand All @@ -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();

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 ( ignoreIds.contains( f.id() ) )
continue;

if ( !f.hasGeometry() )
continue;
if ( !f.hasGeometry() )
continue;

nearGeometries << f.geometry().geometry()->clone();
}
nearGeometries << f.geometry().geometry()->clone();
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/core/geometry/qgsgeometryeditutils.h
Expand Up @@ -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
20 changes: 15 additions & 5 deletions src/core/qgsproject.cpp
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions src/core/qgsproject.h
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down

2 comments on commit 0513bb3

@m-kuhn
Copy link
Member

@m-kuhn m-kuhn commented on 0513bb3 Jan 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see all these changes for the QgsProject!
I wonder what the ultimate goal is. Have a QgisApp::activeProject property (and appropriate QgisInterface members) sounds like what will be required for a MDI?

@wonder-sk
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal is to make at least the core library independent from the QgsProject singleton. The first bit of code to benefit from this should be QGIS server - no need to have special project parsing code and elimination of all sorts of small incompatibilities between QGIS desktop and server.

In the end it would be nice to remove QgsProject::instance() altogether and replace it with QgisApp::activeProject(), but that seems too ambitious at this point (1000+ usages of instance() as of now just in c++ code).

Please sign in to comment.