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 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 - 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) - 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} QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer}

View File

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

View File

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

View File

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

View File

@ -7656,7 +7656,7 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
geom = newGeometry; geom = newGeometry;
} }
// avoid intersection if enabled in digitize settings // 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 // 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; delete g;
QgsGeometry featGeom = f->geometry(); QgsGeometry featGeom = f->geometry();
int avoidIntersectionsReturn = featGeom.avoidIntersections(); int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
f->setGeometry( featGeom ); f->setGeometry( featGeom );
if ( avoidIntersectionsReturn == 1 ) if ( avoidIntersectionsReturn == 1 )
{ {
@ -295,17 +295,14 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e )
//use always topological editing for avoidIntersection. //use always topological editing for avoidIntersection.
//Otherwise, no way to guarantee the geometries don't have a small gap in between. //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(); bool avoidIntersection = !intersectionLayers.isEmpty();
if ( avoidIntersection ) //try to add topological points also to background layers if ( avoidIntersection ) //try to add topological points also to background layers
{ {
QStringList::const_iterator lIt = intersectionLayers.constBegin(); Q_FOREACH ( QgsVectorLayer* vl, intersectionLayers )
for ( ; lIt != intersectionLayers.constEnd(); ++lIt )
{ {
QgsMapLayer* ml = QgsProject::instance()->mapLayer( *lIt );
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml );
//can only add topological points if background layer is editable... //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() ); vl->addTopologicalPoints( f->geometry() );
} }

View File

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

View File

@ -17,6 +17,7 @@
#include "qgsfeatureiterator.h" #include "qgsfeatureiterator.h"
#include "qgsgeometry.h" #include "qgsgeometry.h"
#include "qgsmapcanvas.h" #include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h" #include "qgsvectorlayer.h"
#include "qgisapp.h" #include "qgisapp.h"
@ -108,7 +109,7 @@ void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
QHash<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures; QHash<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures;
ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() ); 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 ); emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL );
vlayer->destroyEditCommand(); vlayer->destroyEditCommand();

View File

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

View File

@ -1871,14 +1871,14 @@ bool QgsGeometry::deletePart( int partNum )
return ok; 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 ) if ( !d->geometry )
{ {
return 1; return 1;
} }
QgsAbstractGeometry* diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), ignoreFeatures ); QgsAbstractGeometry* diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, ignoreFeatures );
if ( diffGeom ) if ( diffGeom )
{ {
detach( false ); detach( false );

View File

@ -795,10 +795,12 @@ class CORE_EXPORT QgsGeometry
* 1 if geometry is not of polygon type, * 1 if geometry is not of polygon type,
* 2 if avoid intersection would change the geometry type, * 2 if avoid intersection would change the geometry type,
* 3 other error during intersection removal * 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) * @param ignoreFeatures possibility to give a list of features where intersections should be ignored (not available in python bindings)
* @note added in 1.5 * @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 /** \ingroup core
*/ */

View File

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

View File

@ -1023,15 +1023,25 @@ void QgsProject::setCustomVariables( const QVariantMap& variables )
emit customVariablesChanged(); 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 ); QStringList list;
emit avoidIntersectionsListChanged(); Q_FOREACH ( QgsVectorLayer* layer, layers )
list << layer->id();
writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
emit avoidIntersectionsLayersChanged();
} }
QgsExpressionContext QgsProject::createExpressionContext() const 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( QgsCoordinateReferenceSystem crs READ crs WRITE setCrs )
Q_PROPERTY( QgsMapThemeCollection* mapThemeCollection READ mapThemeCollection NOTIFY mapThemeCollectionChanged ) Q_PROPERTY( QgsMapThemeCollection* mapThemeCollection READ mapThemeCollection NOTIFY mapThemeCollectionChanged )
Q_PROPERTY( QgsSnappingConfig snappingConfig READ snappingConfig WRITE setSnappingConfig NOTIFY snappingConfigChanged ) 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: public:
//! Returns the QgsProject singleton instance //! Returns the QgsProject singleton instance
@ -465,14 +465,14 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
* *
* @note Added in QGIS 3.0 * @note Added in QGIS 3.0
*/ */
QStringList avoidIntersectionsList() const; QList<QgsVectorLayer*> avoidIntersectionsLayers() const;
/** /**
* A list of layers with which intersections should be avoided. * A list of layers with which intersections should be avoided.
* *
* @note Added in QGIS 3.0 * @note Added in QGIS 3.0
*/ */
void setAvoidIntersectionsList( const QStringList& avoidIntersectionsList ); void setAvoidIntersectionsLayers( const QList<QgsVectorLayer*>& layers );
/** /**
* A map of custom project variables. * A map of custom project variables.
@ -760,11 +760,11 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
void topologicalEditingChanged(); void topologicalEditingChanged();
/** /**
* Emitted whenever avoidIntersectionsList has changed. * Emitted whenever avoidIntersectionsLayers has changed.
* *
* @note Added in QGIS 3.0 * @note Added in QGIS 3.0
*/ */
void avoidIntersectionsListChanged(); void avoidIntersectionsLayersChanged();
/** /**
* Emitted when the map theme collection changes. * Emitted when the map theme collection changes.