Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add test
Add onStyleChanged
Add if mRenderContext when necessary
Orderer renderer if and add enableInvisibleFeature
  • Loading branch information
lbartoletti committed Sep 25, 2018
1 parent 458fa17 commit 9bbabcf
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 5 deletions.
2 changes: 2 additions & 0 deletions python/core/auto_generated/qgstracer.sip.in
Expand Up @@ -58,6 +58,8 @@ Sets the ``crs`` and ``transformContext`` used for tracing.
void setRenderContext( const QgsRenderContext *renderContext );
%Docstring
Sets the ``renderContext`` used for tracing only on visible features.

.. versionadded:: 3.4
%End

QgsRectangle extent() const;
Expand Down
20 changes: 15 additions & 5 deletions src/core/qgstracer.cpp
Expand Up @@ -24,6 +24,7 @@
#include "qgsvectorlayer.h"
#include "qgsexception.h"
#include "qgsrenderer.h"
#include "qgssettings.h"

#include <queue>
#include <vector>
Expand Down Expand Up @@ -459,8 +460,7 @@ QgsTracer::QgsTracer() = default;
bool QgsTracer::initGraph()
{
if ( mGraph )
return true;
// already initialized
return true; // already initialized

mHasTopologyProblem = false;

Expand All @@ -475,15 +475,17 @@ bool QgsTracer::initGraph()

t1.start();
int featuresCounted = 0;
bool enableInvisibleFeature = QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool();
for ( QgsVectorLayer *vl : qgis::as_const( mLayers ) )
{
QgsFeatureRequest request;

bool filter = false;
std::unique_ptr< QgsFeatureRenderer > renderer( vl->renderer() ? vl->renderer()->clone() : nullptr );
std::unique_ptr< QgsFeatureRenderer > renderer;
QgsRenderContext *ctx = nullptr;
if ( mRenderContext )
if ( !enableInvisibleFeature && mRenderContext )
{
renderer.reset( vl->renderer() ? vl->renderer()->clone() : nullptr );
mRenderContext->expressionContext() << QgsExpressionContextUtils::layerScope( vl );
ctx = mRenderContext.get();
if ( renderer )
Expand Down Expand Up @@ -602,6 +604,7 @@ void QgsTracer::setLayers( const QList<QgsVectorLayer *> &layers )
disconnect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
disconnect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
disconnect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
disconnect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
disconnect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
}

Expand Down Expand Up @@ -702,14 +705,21 @@ void QgsTracer::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVaria
Q_UNUSED( fid );
Q_UNUSED( idx );
Q_UNUSED( value );
invalidateGraph();
if ( mRenderContext )
invalidateGraph();
}

void QgsTracer::onDataChanged( )
{
invalidateGraph();
}

void QgsTracer::onStyleChanged( )
{
if ( mRenderContext )
invalidateGraph();
}

void QgsTracer::onLayerDestroyed( QObject *obj )
{
// remove the layer before it is completely invalid (static_cast should be the safest cast)
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgstracer.h
Expand Up @@ -70,6 +70,7 @@ class CORE_EXPORT QgsTracer : public QObject

/**
* Sets the \a renderContext used for tracing only on visible features.
* \since QGIS 3.4
*/
void setRenderContext( const QgsRenderContext *renderContext );

Expand Down Expand Up @@ -169,6 +170,7 @@ class CORE_EXPORT QgsTracer : public QObject
void onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom );
void onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value );
void onDataChanged( );
void onStyleChanged( );
void onLayerDestroyed( QObject *obj );

private:
Expand Down
98 changes: 98 additions & 0 deletions tests/src/core/testqgstracer.cpp
Expand Up @@ -20,6 +20,12 @@
#include <qgstracer.h>
#include <qgsvectorlayer.h>
#include "qgsproject.h"
#include "qgscategorizedsymbolrenderer.h"
#include "qgssettings.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgsmapsettings.h"
#include "qgssnappingutils.h"

class TestQgsTracer : public QObject
{
Expand All @@ -36,6 +42,7 @@ class TestQgsTracer : public QObject
void testReprojection();
void testCurved();
void testOffset();
void testInvisible();

private:

Expand Down Expand Up @@ -156,6 +163,97 @@ void TestQgsTracer::testSimple()
delete vl;
}

void TestQgsTracer::testInvisible()
{
QgsVectorLayer *mVL = new QgsVectorLayer( QStringLiteral( "Linestring?field=fld:int" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) );
QgsFeature f1, f2, f3, f4;
int idx = mVL->fields().indexFromName( QStringLiteral( "fld" ) );
QVERIFY( idx != -1 );
f1.initAttributes( 1 );
f2.initAttributes( 1 );
f3.initAttributes( 1 );
f4.initAttributes( 1 );

/* This shape - nearly a square (one side is shifted to have exactly one shortest
* path between corners):
* 0,10 +----+ 20,10
* | /
* 0,0 +--+ 10,0
*/
QgsGeometry geom = QgsGeometry::fromWkt( "LINESTRING(0 0, 0 10)" );
f1.setGeometry( geom );
f1.setAttribute( idx, QVariant( 2 ) );
geom = QgsGeometry::fromWkt( "LINESTRING(0 0, 10 0)" );
f2.setGeometry( geom );
f2.setAttribute( idx, QVariant( 1 ) );
geom = QgsGeometry::fromWkt( "LINESTRING(0 10, 20 10)" );
f3.setGeometry( geom );
f3.setAttribute( idx, QVariant( 1 ) );
geom = QgsGeometry::fromWkt( "LINESTRING(10 0, 20 10)" );
f4.setGeometry( geom );
f4.setAttribute( idx, QVariant( 1 ) );
QgsFeatureList flist;
flist << f1 << f2 << f3 << f4;

mVL->dataProvider()->addFeatures( flist );

QgsProject::instance()->addMapLayer( mVL );

QgsCategorizedSymbolRenderer *renderer = new QgsCategorizedSymbolRenderer();
renderer->setClassAttribute( QStringLiteral( "fld" ) );
renderer->setSourceSymbol( QgsSymbol::defaultSymbol( QgsWkbTypes::LineGeometry ) );
renderer->addCategory( QgsRendererCategory( "2", QgsSymbol::defaultSymbol( QgsWkbTypes::LineGeometry ), QStringLiteral( "2" ) ) );
mVL->setRenderer( renderer );

//create legend with symbology nodes for categorized renderer
QgsLayerTree *root = new QgsLayerTree();
QgsLayerTreeLayer *n = new QgsLayerTreeLayer( mVL );
root->addChildNode( n );
QgsLayerTreeModel *m = new QgsLayerTreeModel( root, nullptr );
m->refreshLayerLegend( n );

QList<QgsLayerTreeModelLegendNode *> nodes = m->layerLegendNodes( n );
QCOMPARE( nodes.length(), 1 );
//uncheck all and test that all nodes are unchecked
static_cast< QgsSymbolLegendNode * >( nodes.at( 0 ) )->uncheckAllItems();
Q_FOREACH ( QgsLayerTreeModelLegendNode *ln, nodes )
{
QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Unchecked );
}

QgsMapSettings mapSettings;
mapSettings.setOutputSize( QSize( 100, 100 ) );
mapSettings.setExtent( QgsRectangle( 0, 0, 1, 1 ) );
QVERIFY( mapSettings.hasValidSettings() );
mapSettings.setLayers( QList<QgsMapLayer *>() << mVL );

QgsSnappingUtils u;
u.setMapSettings( mapSettings );
u.setEnableSnappingForInvisibleFeature( false );
u.setCurrentLayer( mVL );

QgsSnappingConfig snappingConfig = u.config();
snappingConfig.setEnabled( true );
snappingConfig.setTolerance( 10 );
snappingConfig.setUnits( QgsTolerance::Pixels );
snappingConfig.setMode( QgsSnappingConfig::ActiveLayer );
u.setConfig( snappingConfig );
QgsTracer tracer;
tracer.setLayers( QList<QgsVectorLayer *>() << mVL );

QgsPolylineXY points1 = tracer.findShortestPath( QgsPointXY( 10, 0 ), QgsPointXY( 0, 10 ) );
QCOMPARE( points1.count(), 3 );
QCOMPARE( points1[0], QgsPointXY( 10, 0 ) );
QCOMPARE( points1[1], QgsPointXY( 0, 0 ) );
QCOMPARE( points1[2], QgsPointXY( 0, 10 ) );

QgsRenderContext renderContext = QgsRenderContext::fromMapSettings( mapSettings );
tracer.setRenderContext( &renderContext );
points1 = tracer.findShortestPath( QgsPointXY( 10, 0 ), QgsPointXY( 0, 10 ) );
QCOMPARE( points1.count(), 0 );

}

void TestQgsTracer::testPolygon()
{
// the same shape as in testSimple() but with just one polygon ring
Expand Down

0 comments on commit 9bbabcf

Please sign in to comment.