Skip to content

Commit bf0bfd7

Browse files
committed
Data defined symbology for marker symbol layer
1 parent 31a3000 commit bf0bfd7

File tree

6 files changed

+361
-96
lines changed

6 files changed

+361
-96
lines changed

src/core/symbology-ng/qgslinesymbollayerv2.cpp

+199-14
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ class MyLine
612612
};
613613

614614

615-
QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
615+
QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval ): mIntervalExpression( 0 ), mOffsetExpression( 0 ), mPlacementExpression( 0 )
616616
{
617617
mRotateMarker = rotateMarker;
618618
mInterval = interval;
@@ -630,6 +630,89 @@ QgsMarkerLineSymbolLayerV2::~QgsMarkerLineSymbolLayerV2()
630630
delete mMarker;
631631
}
632632

633+
const QgsExpression* QgsMarkerLineSymbolLayerV2::dataDefinedProperty( const QString& property ) const
634+
{
635+
if ( property == "interval" )
636+
{
637+
return mIntervalExpression;
638+
}
639+
else if ( property == "offset" )
640+
{
641+
return mOffsetExpression;
642+
}
643+
else if ( property == "placement" )
644+
{
645+
return mPlacementExpression;
646+
}
647+
return 0;
648+
}
649+
650+
QString QgsMarkerLineSymbolLayerV2::dataDefinedPropertyString( const QString& property ) const
651+
{
652+
const QgsExpression* ex = dataDefinedProperty( property );
653+
return ex ? ex->dump() : QString();
654+
}
655+
656+
void QgsMarkerLineSymbolLayerV2::setDataDefinedProperty( const QString& property, const QString& expressionString )
657+
{
658+
if ( property == "interval" )
659+
{
660+
delete mIntervalExpression; mIntervalExpression = new QgsExpression( expressionString );
661+
}
662+
else if ( property == "offset" )
663+
{
664+
delete mOffsetExpression; mOffsetExpression = new QgsExpression( expressionString );
665+
}
666+
else if ( property == "placement" )
667+
{
668+
delete mPlacementExpression; mPlacementExpression = new QgsExpression( expressionString );
669+
}
670+
}
671+
672+
void QgsMarkerLineSymbolLayerV2::removeDataDefinedProperty( const QString& property )
673+
{
674+
if ( property == "interval" )
675+
{
676+
delete mIntervalExpression; mIntervalExpression = 0;
677+
}
678+
else if ( property == "offset" )
679+
{
680+
delete mOffsetExpression; mOffsetExpression = 0;
681+
}
682+
else if ( property == "placement" )
683+
{
684+
delete mPlacementExpression; mPlacementExpression = 0;
685+
}
686+
}
687+
688+
void QgsMarkerLineSymbolLayerV2::removeDataDefinedProperties()
689+
{
690+
delete mIntervalExpression; mIntervalExpression = 0;
691+
delete mOffsetExpression; mOffsetExpression = 0;
692+
delete mPlacementExpression; mPlacementExpression = 0;
693+
}
694+
695+
QSet<QString> QgsMarkerLineSymbolLayerV2::usedAttributes() const
696+
{
697+
QSet<QString> attributes;
698+
699+
//add data defined attributes
700+
QStringList columns;
701+
if ( mIntervalExpression )
702+
columns.append( mIntervalExpression->referencedColumns() );
703+
if ( mOffsetExpression )
704+
columns.append( mOffsetExpression->referencedColumns() );
705+
if ( mPlacementExpression )
706+
columns.append( mPlacementExpression->referencedColumns() );
707+
708+
QStringList::const_iterator it = columns.constBegin();
709+
for ( ; it != columns.constEnd(); ++it )
710+
{
711+
attributes.insert( *it );
712+
}
713+
return attributes;
714+
}
715+
633716
QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props )
634717
{
635718
bool rotate = DEFAULT_MARKERLINE_ROTATE;
@@ -667,6 +750,21 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props
667750
else
668751
x->setPlacement( Interval );
669752
}
753+
754+
//data defined properties
755+
if ( props.contains( "interval_expression" ) )
756+
{
757+
x->setDataDefinedProperty( "interval", props["interval_expression"] );
758+
}
759+
if ( props.contains( "offset_expression" ) )
760+
{
761+
x->setDataDefinedProperty( "offset", props["offset_expression"] );
762+
}
763+
if ( props.contains( "placement_expression" ) )
764+
{
765+
x->setDataDefinedProperty( "placement", props["placement_expression"] );
766+
}
767+
670768
return x;
671769
}
672770

@@ -694,6 +792,9 @@ void QgsMarkerLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context
694792
mMarker->setRenderHints( hints );
695793

696794
mMarker->startRender( context.renderContext() );
795+
796+
//prepare expressions for data defined properties
797+
prepareExpressions( context.layer() );
697798
}
698799

699800
void QgsMarkerLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
@@ -703,24 +804,56 @@ void QgsMarkerLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
703804

704805
void QgsMarkerLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
705806
{
706-
if ( mOffset == 0 )
807+
double offset = mOffset;
808+
if ( mOffsetExpression )
809+
{
810+
offset = mOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
811+
}
812+
813+
Placement placement = mPlacement;
814+
if ( mPlacementExpression )
815+
{
816+
QString placementString = mPlacementExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
817+
if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
818+
{
819+
placement = Vertex;
820+
}
821+
else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
822+
{
823+
placement = LastVertex;
824+
}
825+
else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
826+
{
827+
placement = FirstVertex;
828+
}
829+
else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
830+
{
831+
placement = CentralPoint;
832+
}
833+
else
834+
{
835+
placement = Interval;
836+
}
837+
}
838+
839+
if ( offset == 0 )
707840
{
708-
if ( mPlacement == Interval )
841+
if ( placement == Interval )
709842
renderPolylineInterval( points, context );
710-
else if ( mPlacement == CentralPoint )
843+
else if ( placement == CentralPoint )
711844
renderPolylineCentral( points, context );
712845
else
713-
renderPolylineVertex( points, context );
846+
renderPolylineVertex( points, context, placement );
714847
}
715848
else
716849
{
717-
QPolygonF points2 = ::offsetLine( points, mOffset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ) );
718-
if ( mPlacement == Interval )
850+
QPolygonF points2 = ::offsetLine( points, offset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ) );
851+
if ( placement == Interval )
719852
renderPolylineInterval( points2, context );
720-
else if ( mPlacement == CentralPoint )
853+
else if ( placement == CentralPoint )
721854
renderPolylineCentral( points2, context );
722855
else
723-
renderPolylineVertex( points2, context );
856+
renderPolylineVertex( points2, context, placement );
724857
}
725858
}
726859

@@ -735,7 +868,16 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineInterval( const QPolygonF& points
735868
double origAngle = mMarker->angle();
736869

737870
QgsRenderContext& rc = context.renderContext();
738-
double interval = mInterval > 0 ? mInterval : 0.1;
871+
double interval = mInterval;
872+
if ( mIntervalExpression )
873+
{
874+
interval = mIntervalExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
875+
}
876+
if ( !interval > 0 )
877+
{
878+
interval = 0.1;
879+
}
880+
739881
double painterUnitInterval = interval * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit );
740882

741883
for ( int i = 1; i < points.count(); ++i )
@@ -796,7 +938,7 @@ static double _averageAngle( const QPointF& prevPt, const QPointF& pt, const QPo
796938
return atan2( unitY, unitX );
797939
}
798940

799-
void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context )
941+
void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement )
800942
{
801943
if ( points.isEmpty() )
802944
return;
@@ -808,12 +950,12 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points,
808950
int i, maxCount;
809951
bool isRing = false;
810952

811-
if ( mPlacement == FirstVertex )
953+
if ( placement == FirstVertex )
812954
{
813955
i = 0;
814956
maxCount = 1;
815957
}
816-
else if ( mPlacement == LastVertex )
958+
else if ( placement == LastVertex )
817959
{
818960
i = points.count() - 1;
819961
maxCount = points.count();
@@ -955,6 +1097,19 @@ QgsStringMap QgsMarkerLineSymbolLayerV2::properties() const
9551097
else
9561098
map["placement"] = "interval";
9571099

1100+
if ( mIntervalExpression )
1101+
{
1102+
map["interval_expression"] = mIntervalExpression->dump();
1103+
}
1104+
if ( mOffsetExpression )
1105+
{
1106+
map["offset_expression"] = mOffsetExpression->dump();
1107+
}
1108+
if ( mPlacementExpression )
1109+
{
1110+
map["placement_expression"] = mPlacementExpression->dump();
1111+
}
1112+
9581113
return map;
9591114
}
9601115

@@ -985,6 +1140,21 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::clone() const
9851140
x->setPlacement( mPlacement );
9861141
x->setOffsetUnit( mOffsetUnit );
9871142
x->setIntervalUnit( mIntervalUnit );
1143+
1144+
//data defined properties
1145+
if ( mIntervalExpression )
1146+
{
1147+
x->setDataDefinedProperty( "interval", mIntervalExpression->dump() );
1148+
}
1149+
if ( mOffsetExpression )
1150+
{
1151+
x->setDataDefinedProperty( "offset", mOffsetExpression->dump() );
1152+
}
1153+
if ( mPlacementExpression )
1154+
{
1155+
x->setDataDefinedProperty( "placement", mPlacementExpression->dump() );
1156+
}
1157+
9881158
return x;
9891159
}
9901160

@@ -1162,6 +1332,22 @@ QgsSymbolV2::OutputUnit QgsMarkerLineSymbolLayerV2::outputUnit() const
11621332
return unit;
11631333
}
11641334

1335+
void QgsMarkerLineSymbolLayerV2::prepareExpressions( const QgsVectorLayer* vl )
1336+
{
1337+
if ( !vl )
1338+
{
1339+
return;
1340+
}
1341+
1342+
const QgsFields& fields = vl->pendingFields();
1343+
if ( mIntervalExpression )
1344+
mIntervalExpression->prepare( fields );
1345+
if ( mOffsetExpression )
1346+
mOffsetExpression->prepare( fields );
1347+
if ( mPlacementExpression )
1348+
mPlacementExpression->prepare( fields );
1349+
}
1350+
11651351
/////////////
11661352

11671353
QgsLineDecorationSymbolLayerV2::QgsLineDecorationSymbolLayerV2( QColor color, double width )
@@ -1191,7 +1377,6 @@ QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::create( const QgsStringMap& pr
11911377
layer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
11921378
}
11931379
return layer;
1194-
11951380
}
11961381

11971382
QString QgsLineDecorationSymbolLayerV2::layerType() const

src/core/symbology-ng/qgslinesymbollayerv2.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,18 @@ class CORE_EXPORT QgsMarkerLineSymbolLayerV2 : public QgsLineSymbolLayerV2
203203
void setOutputUnit( QgsSymbolV2::OutputUnit unit );
204204
QgsSymbolV2::OutputUnit outputUnit() const;
205205

206+
const QgsExpression* dataDefinedProperty( const QString& property ) const;
207+
QString dataDefinedPropertyString( const QString& property ) const;
208+
void setDataDefinedProperty( const QString& property, const QString& expressionString );
209+
void removeDataDefinedProperty( const QString& property );
210+
void removeDataDefinedProperties();
211+
212+
QSet<QString> usedAttributes() const;
213+
206214
protected:
207215

208216
void renderPolylineInterval( const QPolygonF& points, QgsSymbolV2RenderContext& context );
209-
void renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context );
217+
void renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement = Vertex );
210218
void renderPolylineCentral( const QPolygonF& points, QgsSymbolV2RenderContext& context );
211219

212220
bool mRotateMarker;
@@ -216,6 +224,15 @@ class CORE_EXPORT QgsMarkerLineSymbolLayerV2 : public QgsLineSymbolLayerV2
216224
double mOffset;
217225
QgsSymbolV2::OutputUnit mOffsetUnit;
218226
Placement mPlacement;
227+
228+
//data defined properties
229+
QgsExpression* mIntervalExpression;
230+
QgsExpression* mOffsetExpression;
231+
QgsExpression* mPlacementExpression;
232+
233+
private:
234+
//helper functions for data defined symbology
235+
void prepareExpressions( const QgsVectorLayer* vl );
219236
};
220237

221238
/////////

src/core/symbology-ng/qgssymbollayerv2.h

+7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class QPainter;
3232
class QSize;
3333
class QPolygonF;
3434

35+
class QgsExpression;
3536
class QgsRenderContext;
3637

3738
class CORE_EXPORT QgsSymbolLayerV2
@@ -79,6 +80,12 @@ class CORE_EXPORT QgsSymbolLayerV2
7980
// symbol layers normally only use additional attributes to provide data defined settings
8081
virtual QSet<QString> usedAttributes() const { return QSet<QString>(); }
8182

83+
virtual const QgsExpression* dataDefinedProperty( const QString& property ) const { Q_UNUSED( property ); return 0; } //= 0;
84+
virtual QString dataDefinedPropertyString( const QString& property ) const { Q_UNUSED( property ); return QString(); } //= 0;
85+
virtual void setDataDefinedProperty( const QString& property, const QString& expressionString ) { Q_UNUSED( property ); Q_UNUSED( expressionString ); } //=0;
86+
virtual void removeDataDefinedProperty( const QString& property ) { Q_UNUSED( property ); } //=0;
87+
virtual void removeDataDefinedProperties() {} //=0;
88+
8289
protected:
8390
QgsSymbolLayerV2( QgsSymbolV2::SymbolType type, bool locked = false )
8491
: mType( type ), mLocked( locked ), mRenderingPass( 0 ) {}

0 commit comments

Comments
 (0)