Skip to content
Permalink
Browse files

Expression variable for the currently rendered part

During rendering, two new variables will be available:

  * `geometry_part_count`
  * `geometry_part_num` (1-based index)

Useful to apply different styles to different parts of multipart
features
  • Loading branch information
m-kuhn committed Jan 8, 2016
1 parent 3da051b commit 4e9afcec4cd2abde9656ba92d87e6f587be9bbd5
@@ -344,6 +344,11 @@ class QgsExpressionContext
*/
void appendScope( QgsExpressionContextScope* scope /Transfer/ );

/**
* Remove the last scope from the expression context.
*/
void popScope();

/** Appends a scope to the end of the context. This scope will override
* any matching variables or functions provided by existing scopes within the
* context. Ownership of the scope is transferred to the stack.
@@ -191,6 +191,13 @@ class QgsSymbolV2
*/
void renderFeature( const QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false, int currentVertexMarkerType = 0, int currentVertexMarkerSize = 0 );

/**
* Returns the symbol render context. Only valid between startRender and stopRender calls.
*
* @return The symbol render context
*/
QgsSymbolV2RenderContext* symbolRenderContext();

protected:
QgsSymbolV2( SymbolType type, const QgsSymbolLayerV2List& layers /Transfer/ ); // can't be instantiated

@@ -283,6 +290,21 @@ class QgsSymbolV2RenderContext

// workaround for sip 4.7. Don't use assignment - will fail with assertion error
// QgsSymbolV2RenderContext& operator=( const QgsSymbolV2RenderContext& );

/**
* This scope is always available when a symbol of this type is being rendered.
*
* @return An expression scope for details about this symbol
*/
QgsExpressionContextScope* expressionContextScope();
/**
* Set an expression scope for this symbol.
*
* Will take ownership.
*
* @param contextScope An expression scope for details about this symbol
*/
void setExpressionContextScope( QgsExpressionContextScope* contextScope /Transfer/);
};


@@ -381,6 +381,12 @@ void QgsExpressionContext::appendScope( QgsExpressionContextScope* scope )
mStack.append( scope );
}

void QgsExpressionContext::popScope()
{
if ( !mStack.isEmpty() )
mStack.pop_back();
}

QgsExpressionContext& QgsExpressionContext::operator<<( QgsExpressionContextScope* scope )
{
mStack.append( scope );
@@ -378,6 +378,11 @@ class CORE_EXPORT QgsExpressionContext
*/
void appendScope( QgsExpressionContextScope* scope );

/**
* Remove the last scope from the expression context.
*/
void popScope();

/** Appends a scope to the end of the context. This scope will override
* any matching variables or functions provided by existing scopes within the
* context. Ownership of the scope is transferred to the stack.
@@ -186,17 +186,20 @@ bool QgsGeometryGeneratorSymbolLayerV2::isCompatibleWithSymbol( QgsSymbolV2* sym
Q_UNUSED( symbol )
return true;
}

void QgsGeometryGeneratorSymbolLayerV2::render( QgsSymbolV2RenderContext& context )
{
QgsGeometry geom = mExpression->evaluate( &context.renderContext().expressionContext() ).value<QgsGeometry>();
if ( context.feature() )
{
QgsFeature f = *context.feature();
QgsExpressionContext& expressionContext = context.renderContext().expressionContext();

QgsFeature f = expressionContext.feature();
QgsGeometry geom = mExpression->evaluate( &expressionContext ).value<QgsGeometry>();
f.setGeometry( geom );

QgsExpressionContextScope* subSymbolExpressionContextScope = mSymbol->symbolRenderContext()->expressionContextScope();

subSymbolExpressionContextScope->setFeature( f );

mSymbol->renderFeature( f, context.renderContext() );
}
}


@@ -88,6 +88,7 @@ QgsSymbolV2::QgsSymbolV2( SymbolType type, const QgsSymbolLayerV2List& layers )
, mRenderHints( 0 )
, mClipFeaturesToExtent( true )
, mLayer( nullptr )
, mSymbolRenderContext( nullptr )
{

// check they're all correct symbol layers
@@ -235,6 +236,7 @@ const unsigned char* QgsSymbolV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>&

QgsSymbolV2::~QgsSymbolV2()
{
delete mSymbolRenderContext;
// delete all symbol layers (we own them, so it's okay)
qDeleteAll( mLayers );
}
@@ -434,19 +436,27 @@ bool QgsSymbolV2::changeSymbolLayer( int index, QgsSymbolLayerV2* layer )

void QgsSymbolV2::startRender( QgsRenderContext& context, const QgsFields* fields )
{
delete mSymbolRenderContext;
mSymbolRenderContext = new QgsSymbolV2RenderContext( context, outputUnit(), mAlpha, false, mRenderHints, nullptr, fields, mapUnitScale() );

QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints, nullptr, fields, mapUnitScale() );

QgsExpressionContextScope* scope = new QgsExpressionContextScope( QApplication::translate( "QgsSymbolV2", "Symbol Scope" ) );

mSymbolRenderContext->setExpressionContextScope( scope );

Q_FOREACH ( QgsSymbolLayerV2* layer, mLayers )
layer->startRender( symbolContext );
}

void QgsSymbolV2::stopRender( QgsRenderContext& context )
{
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints, nullptr, nullptr, mapUnitScale() );

Q_UNUSED( context )
Q_FOREACH ( QgsSymbolLayerV2* layer, mLayers )
layer->stopRender( symbolContext );
layer->stopRender( *mSymbolRenderContext );

delete mSymbolRenderContext;
mSymbolRenderContext = nullptr;

mLayer = nullptr;
}
@@ -701,6 +711,10 @@ void QgsSymbolV2::renderFeature( const QgsFeature& feature, QgsRenderContext& co
deleteSegmentizedGeometry = true;
}

context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_count", segmentizedGeometry->geometry()->partCount() );
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_num", 1 );

switch ( QgsWKBTypes::flatType( segmentizedGeometry->geometry()->wkbType() ) )
{
case QgsWKBTypes::Point:
@@ -766,6 +780,8 @@ void QgsSymbolV2::renderFeature( const QgsFeature& feature, QgsRenderContext& co

for ( int i = 0; i < mp->numGeometries(); ++i )
{
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_num", i + 1 );

const QgsPointV2* point = static_cast< const QgsPointV2* >( mp->geometryN( i ) );
_getPoint( pt, context, point );
static_cast<QgsMarkerSymbolV2*>( this )->renderPoint( pt, &feature, context, layer, selected );
@@ -793,6 +809,8 @@ void QgsSymbolV2::renderFeature( const QgsFeature& feature, QgsRenderContext& co

for ( unsigned int i = 0; i < num; ++i )
{
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_num", i + 1 );

if ( geomCollection )
{
context.setGeometry( geomCollection->geometryN( i ) );
@@ -824,6 +842,8 @@ void QgsSymbolV2::renderFeature( const QgsFeature& feature, QgsRenderContext& co

for ( unsigned int i = 0; i < num; ++i )
{
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_num", i + 1 );

if ( geomCollection )
{
context.setGeometry( geomCollection->geometryN( i ) );
@@ -869,13 +889,21 @@ void QgsSymbolV2::renderFeature( const QgsFeature& feature, QgsRenderContext& co
{
delete segmentizedGeometry;
}

context.expressionContext().popScope();
}

QgsSymbolV2RenderContext* QgsSymbolV2::symbolRenderContext()
{
return mSymbolRenderContext;
}

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


QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u, qreal alpha, bool selected, int renderHints, const QgsFeature* f, const QgsFields* fields, const QgsMapUnitScale& mapUnitScale )
: mRenderContext( c ),
mExpressionContextScope( nullptr ),
mOutputUnit( u ),
mMapUnitScale( mapUnitScale ),
mAlpha( alpha ),
@@ -888,7 +916,7 @@ QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymb

QgsSymbolV2RenderContext::~QgsSymbolV2RenderContext()
{

delete mExpressionContextScope;
}

void QgsSymbolV2RenderContext::setOriginalValueVariable( const QVariant& value )
@@ -916,6 +944,16 @@ QgsSymbolV2RenderContext& QgsSymbolV2RenderContext::operator=( const QgsSymbolV2
return *this;
}

QgsExpressionContextScope* QgsSymbolV2RenderContext::expressionContextScope()
{
return mExpressionContextScope;
}

void QgsSymbolV2RenderContext::setExpressionContextScope( QgsExpressionContextScope* contextScope )
{
mExpressionContextScope = contextScope;
}

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

QgsMarkerSymbolV2* QgsMarkerSymbolV2::createSimple( const QgsStringMap& properties )
@@ -241,6 +241,13 @@ class CORE_EXPORT QgsSymbolV2
*/
void renderFeature( const QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false, int currentVertexMarkerType = 0, int currentVertexMarkerSize = 0 );

/**
* Returns the symbol render context. Only valid between startRender and stopRender calls.
*
* @return The symbol render context
*/
QgsSymbolV2RenderContext* symbolRenderContext();

protected:
QgsSymbolV2( SymbolType type, const QgsSymbolLayerV2List& layers ); // can't be instantiated

@@ -310,6 +317,9 @@ class CORE_EXPORT QgsSymbolV2
const QgsVectorLayer* mLayer; //current vectorlayer

private:
//! Initialized in startRender, destroyed in stopRender
QgsSymbolV2RenderContext* mSymbolRenderContext;

Q_DISABLE_COPY( QgsSymbolV2 )

};
@@ -365,8 +375,24 @@ class CORE_EXPORT QgsSymbolV2RenderContext
// workaround for sip 4.7. Don't use assignment - will fail with assertion error
QgsSymbolV2RenderContext& operator=( const QgsSymbolV2RenderContext& );

/**
* This scope is always available when a symbol of this type is being rendered.
*
* @return An expression scope for details about this symbol
*/
QgsExpressionContextScope* expressionContextScope();
/**
* Set an expression scope for this symbol.
*
* Will take ownership.
*
* @param contextScope An expression scope for details about this symbol
*/
void setExpressionContextScope( QgsExpressionContextScope* contextScope );

private:
QgsRenderContext& mRenderContext;
QgsExpressionContextScope* mExpressionContextScope;
QgsSymbolV2::OutputUnit mOutputUnit;
QgsMapUnitScale mMapUnitScale;
qreal mAlpha;
@@ -219,6 +219,9 @@ void TestQgsExpressionContext::contextScopeFunctions()
void TestQgsExpressionContext::contextStack()
{
QgsExpressionContext context;

context.popScope();

//test retrieving from empty context
QVERIFY( !context.hasVariable( "test" ) );
QVERIFY( !context.variable( "test" ).isValid() );
@@ -290,6 +293,11 @@ void TestQgsExpressionContext::contextStack()
scope2->addVariable( QgsExpressionContextScope::StaticVariable( "readonly", 5, true ) );
QVERIFY( context.isReadOnly( "readonly" ) );
QVERIFY( !context.isReadOnly( "test" ) );

// Check scopes can be popped
context.popScope();
QCOMPARE( scopes.length(), 2 );
QCOMPARE( scopes.at( 0 ), scope1 );
}

void TestQgsExpressionContext::contextCopy()
@@ -59,6 +59,7 @@ ADD_PYTHON_TEST(PyQgsShapefileProvider test_provider_shapefile.py)
ADD_PYTHON_TEST(PyQgsSpatialIndex test_qgsspatialindex.py)
ADD_PYTHON_TEST(PyQgsSpatialiteProvider test_provider_spatialite.py)
ADD_PYTHON_TEST(PyQgsSymbolLayerV2 test_qgssymbollayerv2.py)
ADD_PYTHON_TEST(PyQgsSymbolExpressionVariables test_qgssymbolexpressionvariables.py)
ADD_PYTHON_TEST(PyQgsSyntacticSugar test_syntactic_sugar.py)
ADD_PYTHON_TEST(PyQgsVectorColorRamp test_qgsvectorcolorramp.py)
ADD_PYTHON_TEST(PyQgsVectorFileWriter test_qgsvectorfilewriter.py)

0 comments on commit 4e9afce

Please sign in to comment.
You can’t perform that action at this time.