Skip to content
Permalink
Browse files

Make QgsGeometry independent from QgsProject + better avoid intersect…

…ions API
  • Loading branch information
wonder-sk committed Jan 7, 2017
1 parent 8c340f7 commit 0513bb357cc4188147ece774b3c0dd2fed6a1af0
@@ -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}
@@ -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
{
@@ -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.
@@ -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 )
{
@@ -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
@@ -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() );
}
@@ -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 )
@@ -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();
@@ -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;
}
}
@@ -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 );
@@ -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
*/
@@ -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();

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();
}
}

@@ -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
@@ -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
@@ -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.

2 comments on commit 0513bb3

@m-kuhn

This comment has been minimized.

Copy link
Member

@m-kuhn m-kuhn replied Jan 7, 2017

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

This comment has been minimized.

Copy link
Member Author

@wonder-sk wonder-sk replied Jan 8, 2017

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.
You can’t perform that action at this time.