Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Followup 2ac5933 with more data defined fixes
2ac5933 fixed the regression in 2.12, but there were more underlying
issues from <2.8 causing sub symbols with data defined properties
to be ignored.

Add some tests.

(refs #13707)

(cherry-picked from 9e84fca)
  • Loading branch information
nyalldawson committed Nov 18, 2015
1 parent 6bb6929 commit ce2a84d
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 10 deletions.
2 changes: 2 additions & 0 deletions python/core/symbology-ng/qgsfillsymbollayerv2.sip
Expand Up @@ -457,6 +457,8 @@ class QgsImageFillSymbolLayer: QgsFillSymbolLayerV2
virtual double dxfWidth( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const;
virtual QColor dxfColor( QgsSymbolV2RenderContext& context ) const;
virtual Qt::PenStyle dxfPenStyle() const;

QSet<QString> usedAttributes() const;
};

/** \ingroup core
Expand Down
5 changes: 5 additions & 0 deletions python/core/symbology-ng/qgssymbolv2.sip
Expand Up @@ -160,6 +160,11 @@ class QgsSymbolV2

QSet<QString> usedAttributes() const;

/** Returns whether the symbol utilises any data defined properties.
* @note added in QGIS 2.12
*/
bool hasDataDefinedProperties() const;

void setLayer( const QgsVectorLayer* layer );
const QgsVectorLayer* layer() const;

Expand Down
25 changes: 15 additions & 10 deletions src/core/symbology-ng/qgsfillsymbollayerv2.cpp
Expand Up @@ -1701,6 +1701,14 @@ Qt::PenStyle QgsImageFillSymbolLayer::dxfPenStyle() const
#endif //0
}

QSet<QString> QgsImageFillSymbolLayer::usedAttributes() const
{
QSet<QString> attr = QgsFillSymbolLayerV2::usedAttributes();
if ( mOutline )
attr.unite( mOutline->usedAttributes() );
return attr;
}


//QgsSVGFillSymbolLayer

Expand Down Expand Up @@ -2319,7 +2327,7 @@ QgsSymbolV2* QgsLinePatternFillSymbolLayer::subSymbol()

QSet<QString> QgsLinePatternFillSymbolLayer::usedAttributes() const
{
QSet<QString> attr = QgsFillSymbolLayerV2::usedAttributes();
QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes();
if ( mFillLineSymbol )
attr.unite( mFillLineSymbol->usedAttributes() );
return attr;
Expand Down Expand Up @@ -2875,7 +2883,8 @@ QString QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth( double widthScaleFa
void QgsLinePatternFillSymbolLayer::applyDataDefinedSettings( QgsSymbolV2RenderContext &context )
{
if ( !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_LINEANGLE ) && !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISTANCE )
&& !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_LINEWIDTH ) && !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR ) )
&& !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_LINEWIDTH ) && !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR )
&& ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
{
return; //no data defined settings
}
Expand Down Expand Up @@ -3288,14 +3297,12 @@ bool QgsPointPatternFillSymbolLayer::setSubSymbol( QgsSymbolV2* symbol )

void QgsPointPatternFillSymbolLayer::applyDataDefinedSettings( QgsSymbolV2RenderContext &context )
{
#if 0
// TODO: enable but check also if mMarkerSymbol has data defined properties
if ( !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISTANCE_X ) && !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISTANCE_Y )
&& !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISPLACEMENT_X ) && !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISPLACEMENT_Y ) )
&& !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISPLACEMENT_X ) && !hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISPLACEMENT_Y )
&& ( !mMarkerSymbol || !mMarkerSymbol->hasDataDefinedProperties() ) )
{
return;
}
#endif

double distanceX = mDistanceX;
if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_DISTANCE_X ) )
Expand Down Expand Up @@ -3331,7 +3338,7 @@ double QgsPointPatternFillSymbolLayer::estimateMaxBleed() const

QSet<QString> QgsPointPatternFillSymbolLayer::usedAttributes() const
{
QSet<QString> attributes = QgsSymbolLayerV2::usedAttributes();
QSet<QString> attributes = QgsImageFillSymbolLayer::usedAttributes();

if ( mMarkerSymbol )
attributes.unite( mMarkerSymbol->usedAttributes() );
Expand Down Expand Up @@ -3457,9 +3464,7 @@ bool QgsCentroidFillSymbolLayerV2::setSubSymbol( QgsSymbolV2* symbol )

QSet<QString> QgsCentroidFillSymbolLayerV2::usedAttributes() const
{
QSet<QString> attributes;

attributes.unite( QgsSymbolLayerV2::usedAttributes() );
QSet<QString> attributes = QgsFillSymbolLayerV2::usedAttributes();

if ( mMarker )
attributes.unite( mMarker->usedAttributes() );
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology-ng/qgsfillsymbollayerv2.h
Expand Up @@ -571,6 +571,8 @@ class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayerV2
virtual QColor dxfColor( QgsSymbolV2RenderContext& context ) const override;
virtual Qt::PenStyle dxfPenStyle() const override;

QSet<QString> usedAttributes() const override;

protected:
QBrush mBrush;
double mNextAngle; // mAngle / data defined angle
Expand Down
10 changes: 10 additions & 0 deletions src/core/symbology-ng/qgssymbolv2.cpp
Expand Up @@ -490,6 +490,16 @@ QSet<QString> QgsSymbolV2::usedAttributes() const
return attributes;
}

bool QgsSymbolV2::hasDataDefinedProperties() const
{
Q_FOREACH ( QgsSymbolLayerV2* layer, mLayers )
{
if ( layer->hasDataDefinedProperties() )
return true;
}
return false;
}

////////////////////


Expand Down
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgssymbolv2.h
Expand Up @@ -217,6 +217,11 @@ class CORE_EXPORT QgsSymbolV2

QSet<QString> usedAttributes() const;

/** Returns whether the symbol utilises any data defined properties.
* @note added in QGIS 2.12
*/
bool hasDataDefinedProperties() const;

//! @note the layer will be NULL after stopRender
void setLayer( const QgsVectorLayer* layer ) { mLayer = layer; }
const QgsVectorLayer* layer() const { return mLayer; }
Expand Down
2 changes: 2 additions & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -134,6 +134,7 @@ ADD_QGIS_TEST(invertedpolygontest testqgsinvertedpolygonrenderer.cpp )
ADD_QGIS_TEST(labelingenginev2 testqgslabelingenginev2.cpp)
ADD_QGIS_TEST(layertree testqgslayertree.cpp)
ADD_QGIS_TEST(legendrenderertest testqgslegendrenderer.cpp )
ADD_QGIS_TEST(linefillsymboltest testqgslinefillsymbol.cpp )
ADD_QGIS_TEST(maplayerstylemanager testqgsmaplayerstylemanager.cpp )
ADD_QGIS_TEST(maplayertest testqgsmaplayer.cpp)
# ADD_QGIS_TEST(maprendererjobtest testmaprendererjob.cpp )
Expand All @@ -147,6 +148,7 @@ ADD_QGIS_TEST(painteffectregistrytest testqgspainteffectregistry.cpp)
ADD_QGIS_TEST(painteffecttest testqgspainteffect.cpp)
ADD_QGIS_TEST(pallabelingtest testqgspallabeling.cpp)
ADD_QGIS_TEST(pointlocatortest testqgspointlocator.cpp )
ADD_QGIS_TEST(pointpatternfillsymboltest testqgspointpatternfillsymbol.cpp )
ADD_QGIS_TEST(pointtest testqgspoint.cpp)
ADD_QGIS_TEST(projecttest testqgsproject.cpp)
ADD_QGIS_TEST(qgistest testqgis.cpp)
Expand Down
185 changes: 185 additions & 0 deletions tests/src/core/testqgslinefillsymbol.cpp
@@ -0,0 +1,185 @@
/***************************************************************************
testqgslinefillsymbol.cpp
-------------------------
Date : Nov 2015
Copyright : (C) 2015 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtTest/QtTest>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QDesktopServices>

//qgis includes...
#include <qgsmaprenderer.h>
#include <qgsmaplayer.h>
#include <qgsvectorlayer.h>
#include <qgsapplication.h>
#include <qgsproviderregistry.h>
#include <qgsmaplayerregistry.h>
#include <qgssymbolv2.h>
#include <qgssinglesymbolrendererv2.h>
#include <qgsfillsymbollayerv2.h>
#include "qgslinesymbollayerv2.h"
#include "qgsdatadefined.h"

//qgis test includes
#include "qgsrenderchecker.h"

/** \ingroup UnitTests
* This is a unit test for line fill symbol types.
*/
class TestQgsLineFillSymbol : public QObject
{
Q_OBJECT

public:
TestQgsLineFillSymbol()
: mTestHasError( false )
, mpPolysLayer( 0 )
, mLineFill( 0 )
, mFillSymbol( 0 )
, mSymbolRenderer( 0 )
{}

private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init() {} // will be called before each testfunction is executed.
void cleanup() {} // will be called after every testfunction.

void lineFillSymbol();
void dataDefinedSubSymbol();

private:
bool mTestHasError;

bool imageCheck( const QString& theType );
QgsMapSettings mMapSettings;
QgsVectorLayer * mpPolysLayer;
QgsLinePatternFillSymbolLayer* mLineFill;
QgsFillSymbolV2* mFillSymbol;
QgsSingleSymbolRendererV2* mSymbolRenderer;
QString mTestDataDir;
QString mReport;
};


void TestQgsLineFillSymbol::initTestCase()
{
mTestHasError = false;
// init QGIS's paths - true means that all path will be inited from prefix
QgsApplication::init();
QgsApplication::initQgis();
QgsApplication::showSettings();

//create some objects that will be used in all tests...
QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
mTestDataDir = myDataDir + '/';

//
//create a poly layer that will be used in all tests...
//
QString myPolysFileName = mTestDataDir + "polys.shp";
QFileInfo myPolyFileInfo( myPolysFileName );
mpPolysLayer = new QgsVectorLayer( myPolyFileInfo.filePath(),
myPolyFileInfo.completeBaseName(), "ogr" );

QgsVectorSimplifyMethod simplifyMethod;
simplifyMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification );
mpPolysLayer->setSimplifyMethod( simplifyMethod );

// Register the layer with the registry
QgsMapLayerRegistry::instance()->addMapLayers(
QList<QgsMapLayer *>() << mpPolysLayer );

//setup gradient fill
mLineFill = new QgsLinePatternFillSymbolLayer();
mFillSymbol = new QgsFillSymbolV2();
mFillSymbol->changeSymbolLayer( 0, mLineFill );
mSymbolRenderer = new QgsSingleSymbolRendererV2( mFillSymbol );
mpPolysLayer->setRendererV2( mSymbolRenderer );

// We only need maprender instead of mapcanvas
// since maprender does not require a qui
// and is more light weight
//
mMapSettings.setLayers( QStringList() << mpPolysLayer->id() );
mReport += "<h1>Gradient Renderer Tests</h1>\n";

}
void TestQgsLineFillSymbol::cleanupTestCase()
{
QString myReportFile = QDir::tempPath() + "/qgistest.html";
QFile myFile( myReportFile );
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}

QgsApplication::exitQgis();
}

void TestQgsLineFillSymbol::lineFillSymbol()
{
mReport += "<h2>Line fill symbol renderer test</h2>\n";

QgsStringMap properties;
properties.insert( "color", "0,0,0,255" );
properties.insert( "width", "0.3" );
properties.insert( "capstyle", "flat" );
QgsLineSymbolV2* lineSymbol = QgsLineSymbolV2::createSimple( properties );

mLineFill->setSubSymbol( lineSymbol );
QVERIFY( imageCheck( "symbol_linefill" ) );
}

void TestQgsLineFillSymbol::dataDefinedSubSymbol()
{
mReport += "<h2>Line fill symbol data defined sub symbol test</h2>\n";

QgsStringMap properties;
properties.insert( "color", "0,0,0,255" );
properties.insert( "width", "0.3" );
properties.insert( "capstyle", "flat" );
QgsLineSymbolV2* lineSymbol = QgsLineSymbolV2::createSimple( properties );
lineSymbol->symbolLayer( 0 )->setDataDefinedProperty( "color", new QgsDataDefined( QString( "if(\"Name\" ='Lake','#ff0000','#ff00ff')" ) ) );
mLineFill->setSubSymbol( lineSymbol );
QVERIFY( imageCheck( "datadefined_subsymbol" ) );
}

//
// Private helper functions not called directly by CTest
//


bool TestQgsLineFillSymbol::imageCheck( const QString& theTestType )
{
//use the QgsRenderChecker test utility class to
//ensure the rendered output matches our control image
mMapSettings.setExtent( mpPolysLayer->extent() );
mMapSettings.setOutputDpi( 96 );
QgsRenderChecker myChecker;
myChecker.setControlPathPrefix( "symbol_linefill" );
myChecker.setControlName( "expected_" + theTestType );
myChecker.setMapSettings( mMapSettings );
bool myResultFlag = myChecker.runTest( theTestType );
mReport += myChecker.report();
return myResultFlag;
}

QTEST_MAIN( TestQgsLineFillSymbol )
#include "testqgslinefillsymbol.moc"

0 comments on commit ce2a84d

Please sign in to comment.