[FEATURE] Data-defined size and rotation for single symbol renderer in symbology-ng. (ticket #2585)

Developed by for Faunalia (http://www.faunalia.it) with funding from Regione Toscana - Sistema Informativo per
la Gestione del Territorio e dell' Ambiente [RT-SIGTA]".
For the project: "Sviluppo di prodotti software GIS open-source basati sui prodotti
QuantumGIS e Postgis (CIG 037728516E)



git-svn-id: http://svn.osgeo.org/qgis/trunk@13389 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
wonder 2010-04-25 22:32:53 +00:00
parent aa18abc3c6
commit 02df126b90
12 changed files with 525 additions and 132 deletions

View File

@ -133,6 +133,12 @@ public:
QgsSymbolV2* symbol() const;
void setSymbol(QgsSymbolV2* s /Transfer/);
void setRotationField(QString fieldName);
QString rotationField() const;
void setSizeScaleField(QString fieldName);
QString sizeScaleField() const;
virtual QString dump();
virtual QgsFeatureRendererV2* clone() /Factory/;
@ -453,7 +459,7 @@ class QgsSymbolV2RenderContext
%End
public:
QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u , qreal alpha = 1.0, bool selected = false );
QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u , qreal alpha = 1.0, bool selected = false, int renderHints = 0 );
~QgsSymbolV2RenderContext();
QgsRenderContext& renderContext();
@ -468,6 +474,11 @@ class QgsSymbolV2RenderContext
bool selected() const;
void setSelected( bool selected ) const;
int renderHints() const;
void setRenderHints( int hints );
// Colour used for selections
static QColor selectionColor();
double outputLineWidth(double width) const;
@ -510,6 +521,12 @@ public:
Fill
};
enum RenderHint
{
DataDefinedSizeScale = 1,
DataDefinedRotation = 2
};
virtual ~QgsSymbolV2();
//! return new default symbol for specified geometry type
@ -556,6 +573,12 @@ public:
OutputUnit outputUnit() const;
void setOutputUnit( OutputUnit u );
qreal alpha() const;
void setAlpha( qreal alpha );
int renderHints() const;
void setRenderHints( int hints );
protected:
QgsSymbolV2(SymbolType type, QgsSymbolLayerV2List layers /Transfer/); // can't be instantiated

View File

@ -68,102 +68,72 @@ void QgsSimpleMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& contex
mBrush = QBrush( mColor );
mPen = QPen( mBorderColor );
mPen.setWidthF( context.outputLineWidth( mPen.widthF() ) );
QColor selColor = context.selectionColor();
mSelBrush = QBrush( selColor );
mSelPen = QPen( selColor == mColor ? selColor : mBorderColor );
mSelPen.setWidthF( mPen.widthF() );
mPolygon.clear();
bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation;
bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale;
double scaledSize = context.outputPixelSize( mSize );
double half = scaledSize / 2.0;
// use either QPolygonF or QPainterPath for drawing
// TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes
if ( !prepareShape() ) // drawing as a polygon
{
if ( preparePath() ) // drawing as a painter path
{
// some markers can't be drawn as a polygon (circle, cross)
// For these set the selected border color to the selected color
if ( mName == "rectangle" )
{
mPolygon = QPolygonF( QRectF( QPointF( -half, -half ), QPointF( half, half ) ) );
if ( mName != "circle" )
mSelPen.setColor( selColor );
}
else
{
QgsDebugMsg( "unknown symbol" );
return;
}
}
else if ( mName == "diamond" )
{
mPolygon << QPointF( -half, 0 ) << QPointF( 0, half )
<< QPointF( half, 0 ) << QPointF( 0, -half );
}
else if ( mName == "pentagon" )
{
mPolygon << QPointF( half * sin( DEG2RAD( 288.0 ) ), - half * cos( DEG2RAD( 288.0 ) ) )
<< QPointF( half * sin( DEG2RAD( 216.0 ) ), - half * cos( DEG2RAD( 216.0 ) ) )
<< QPointF( half * sin( DEG2RAD( 144.0 ) ), - half * cos( DEG2RAD( 144.0 ) ) )
<< QPointF( half * sin( DEG2RAD( 72.0 ) ), - half * cos( DEG2RAD( 72.0 ) ) )
<< QPointF( 0, - half );
}
else if ( mName == "triangle" )
{
mPolygon << QPointF( -half, half ) << QPointF( half, half ) << QPointF( 0, -half );
}
else if ( mName == "equilateral_triangle" )
{
mPolygon << QPointF( half * sin( DEG2RAD( 240.0 ) ), - half * cos( DEG2RAD( 240.0 ) ) )
<< QPointF( half * sin( DEG2RAD( 120.0 ) ), - half * cos( DEG2RAD( 120.0 ) ) )
<< QPointF( 0, -half );
}
else if ( mName == "star" )
{
double sixth = half / 6;
mPolygon << QPointF( 0, -half )
<< QPointF( -sixth, -sixth )
<< QPointF( -half, -sixth )
<< QPointF( -sixth, 0 )
<< QPointF( -half, half )
<< QPointF( 0, + sixth )
<< QPointF( half, half )
<< QPointF( + sixth, 0 )
<< QPointF( half, -sixth )
<< QPointF( + sixth, -sixth );
}
else if ( mName == "regular_star" )
{
double r = half;
double inner_r = r * cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
QMatrix transform;
mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324
<< QPointF( r * sin( DEG2RAD( 288.0 ) ) , - r * cos( DEG2RAD( 288 ) ) ) // 288
<< QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252
<< QPointF( r * sin( DEG2RAD( 216.0 ) ) , - r * cos( DEG2RAD( 216.0 ) ) ) // 216
<< QPointF( 0, inner_r ) // 180
<< QPointF( r * sin( DEG2RAD( 144.0 ) ) , - r * cos( DEG2RAD( 144.0 ) ) ) // 144
<< QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108
<< QPointF( r * sin( DEG2RAD( 72.0 ) ) , - r * cos( DEG2RAD( 72.0 ) ) ) // 72
<< QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36
<< QPointF( 0, -half ); // 0
}
else if ( mName == "arrow" )
// scale the shape (if the size is not going to be modified)
if ( !hasDataDefinedSize )
{
double eight = half / 4;
double quarter = half / 2;
double scaledSize = context.outputPixelSize( mSize );
double half = scaledSize / 2.0;
transform.scale( half, half );
}
mPolygon << QPointF( 0, -half )
<< QPointF( quarter, -quarter )
<< QPointF( eight, -quarter )
<< QPointF( eight, half )
<< QPointF( -eight, half )
<< QPointF( -eight, -quarter )
<< QPointF( -quarter, -quarter );
// rotate if the rotation is not going to be changed during the rendering
if ( !hasDataDefinedRotation && mAngle != 0 )
{
transform.rotate( mAngle );
}
if ( !mPolygon.isEmpty() )
mPolygon = transform.map( mPolygon );
else
mPath = transform.map( mPath );
if ( !hasDataDefinedRotation && !hasDataDefinedSize )
{
// we can use the cached marker
// TODO: use caching only when drawing to screen (not printer)
prepareCache( context );
}
else
{
// some markers can't be drawn as a polygon (circle, cross)
// For these set the selected border color to the selected color
if ( mName != "circle" ) mSelPen.setColor( selColor );
mCache = QImage();
mSelCache = QImage();
}
}
// rotate if needed
if ( mAngle != 0 )
mPolygon = QMatrix().rotate( mAngle ).map( mPolygon );
// cache the marker
// TODO: use caching only when drawing to screen (not printer)
// TODO: decide whether to use QImage or QPixmap - based on the render context
void QgsSimpleMarkerSymbolLayerV2::prepareCache( QgsSymbolV2RenderContext& context )
{
double scaledSize = context.outputPixelSize( mSize );
// calculate necessary image size for the cache
double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen
@ -185,6 +155,8 @@ void QgsSimpleMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& contex
// Construct the selected version of the Cache
QColor selColor = context.selectionColor();
mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
mSelCache.fill( 0 );
@ -224,6 +196,127 @@ void QgsSimpleMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context
{
}
bool QgsSimpleMarkerSymbolLayerV2::prepareShape()
{
mPolygon.clear();
if ( mName == "rectangle" )
{
mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
return true;
}
else if ( mName == "diamond" )
{
mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
<< QPointF( 1, 0 ) << QPointF( 0, -1 );
return true;
}
else if ( mName == "pentagon" )
{
mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) )
<< QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) )
<< QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) )
<< QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) )
<< QPointF( 0, -1 );
return true;
}
else if ( mName == "triangle" )
{
mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 );
return true;
}
else if ( mName == "equilateral_triangle" )
{
mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) )
<< QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) )
<< QPointF( 0, -1 );
return true;
}
else if ( mName == "star" )
{
double sixth = 1.0 / 3;
mPolygon << QPointF( 0, -1 )
<< QPointF( -sixth, -sixth )
<< QPointF( -1, -sixth )
<< QPointF( -sixth, 0 )
<< QPointF( -1, 1 )
<< QPointF( 0, + sixth )
<< QPointF( 1, 1 )
<< QPointF( + sixth, 0 )
<< QPointF( 1, -sixth )
<< QPointF( + sixth, -sixth );
return true;
}
else if ( mName == "regular_star" )
{
double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324
<< QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) ) // 288
<< QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252
<< QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) ) // 216
<< QPointF( 0, inner_r ) // 180
<< QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) ) // 144
<< QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108
<< QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) ) // 72
<< QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36
<< QPointF( 0, -1 ); // 0
return true;
}
else if ( mName == "arrow" )
{
double eight = 1.0 / 4;
double quarter = 1.0 / 2;
mPolygon << QPointF( 0, -1 )
<< QPointF( 0.5, -0.5 )
<< QPointF( 0.25, -0.25 )
<< QPointF( 0.25, 1 )
<< QPointF( -0.25, 1 )
<< QPointF( -0.25, -0.5 )
<< QPointF( -0.5, -0.5 );
return true;
}
return false;
}
bool QgsSimpleMarkerSymbolLayerV2::preparePath()
{
mPath = QPainterPath();
if ( mName == "circle" )
{
mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
return true;
}
else if ( mName == "cross" )
{
mPath.moveTo( -1, 0 );
mPath.lineTo( 1, 0 ); // horizontal
mPath.moveTo( 0, -1 );
mPath.lineTo( 0, 1 ); // vertical
return true;
}
else if ( mName == "cross2" )
{
mPath.moveTo( -1, -1 );
mPath.lineTo( 1, 1 );
mPath.moveTo( 1, -1 );
mPath.lineTo( -1, 1 );
return true;
}
else if ( mName == "line" )
{
mPath.moveTo( 0, -1 );
mPath.lineTo( 0, 1 ); // vertical line
return true;
}
return false;
}
void QgsSimpleMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context )
{
QgsRenderContext& rc = context.renderContext();
@ -233,11 +326,48 @@ void QgsSimpleMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV
return;
}
QImage &img = context.selected() ? mSelCache : mCache;
double s = img.width() / context.renderContext().rasterScaleFactor();
p->drawImage( QRectF( point.x() - s / 2.0 + context.outputLineWidth( mOffset.x() ),
point.y() - s / 2.0 + context.outputLineWidth( mOffset.y() ),
s, s ), img );
bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation;
bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale;
if ( !hasDataDefinedRotation && !hasDataDefinedSize )
{
// we will use cached image
QImage &img = context.selected() ? mSelCache : mCache;
double s = img.width() / context.renderContext().rasterScaleFactor();
p->drawImage( QRectF( point.x() - s / 2.0 + context.outputLineWidth( mOffset.x() ),
point.y() - s / 2.0 + context.outputLineWidth( mOffset.y() ),
s, s ), img );
}
else
{
QMatrix transform;
// move to the desired position
transform.translate( point.x() + context.outputLineWidth( mOffset.x() ),
point.y() + context.outputLineWidth( mOffset.y() ) );
// resize if necessary
if ( hasDataDefinedSize )
{
double scaledSize = context.outputPixelSize( mSize );
double half = scaledSize / 2.0;
transform.scale( half, half );
}
// rotate if necessary
if ( mAngle != 0 )
{
transform.rotate( mAngle );
}
p->setBrush( context.selected() ? mSelBrush : mBrush );
p->setPen( context.selected() ? mSelPen : mPen );
if ( !mPolygon.isEmpty() )
p->drawPolygon( transform.map( mPolygon ) );
else
p->drawPath( transform.map( mPath ) );
}
}
@ -268,37 +398,8 @@ void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderCon
}
else
{
double scaledSize = context.outputPixelSize( mSize );
double half = scaledSize / 2.0;
if ( mAngle != 0 )
{
p->save();
p->rotate( mAngle );
}
if ( mName == "circle" )
{
p->drawEllipse( QRectF( -half, -half, half*2, half*2 ) ); // x,y,w,h
}
else if ( mName == "cross" )
{
p->drawLine( QPointF( -half, 0 ), QPointF( half, 0 ) ); // horizontal
p->drawLine( QPointF( 0, -half ), QPointF( 0, half ) ); // vertical
}
else if ( mName == "cross2" )
{
p->drawLine( QPointF( -half, -half ), QPointF( half, half ) );
p->drawLine( QPointF( -half, half ), QPointF( half, -half ) );
}
else if ( mName == "line" )
{
p->drawLine( QPointF( 0, -half ), QPointF( 0, half ) ); // vertical line
}
if ( mAngle != 0 )
p->restore();
p->drawPath( mPath );
}
}
@ -363,6 +464,8 @@ void QgsSvgMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
selPainter.setPen( Qt::NoPen );
selPainter.drawEllipse( QPointF( 0, 0 ), pictureSize*0.6, pictureSize*0.6 );
renderer.render( &selPainter, rect );
mOrigSize = mSize; // save in case the size would be data defined
}
void QgsSvgMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
@ -382,15 +485,18 @@ void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2Re
QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
p->translate( point + outputOffset );
if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
{
double s = mSize / mOrigSize;
p->scale( s, s );
}
if ( mAngle != 0 )
p->rotate( mAngle );
QPicture &pct = context.selected() ? mSelPicture : mPicture;
p->drawPicture( 0, 0, pct );
if ( mAngle != 0 )
p->rotate( -mAngle );
p->restore();
}
@ -555,7 +661,7 @@ void QgsFontMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context
QFontMetrics fm( mFont );
mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 );
mOrigSize = mSize; // save in case the size would be data defined
}
void QgsFontMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
@ -572,6 +678,13 @@ void QgsFontMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2R
p->save();
p->translate( point );
if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
{
double s = mSize / mOrigSize;
p->scale( s, s );
}
if ( mAngle != 0 )
p->rotate( mAngle );

View File

@ -53,10 +53,16 @@ class CORE_EXPORT QgsSimpleMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
void drawMarker( QPainter* p, QgsSymbolV2RenderContext& context );
bool prepareShape();
bool preparePath();
void prepareCache( QgsSymbolV2RenderContext& context );
QColor mBorderColor;
QPen mPen;
QBrush mBrush;
QPolygonF mPolygon;
QPainterPath mPath;
QString mName;
QImage mCache;
QPen mSelPen;
@ -114,6 +120,7 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
QString mPath;
QPicture mPicture;
QPicture mSelPicture;
double mOrigSize;
};
@ -170,6 +177,7 @@ class CORE_EXPORT QgsFontMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
QPointF mChrOffset;
QFont mFont;
double mOrigSize;
};

View File

@ -4,11 +4,16 @@
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgslogger.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include <QDomDocument>
#include <QDomElement>
QgsSingleSymbolRendererV2::QgsSingleSymbolRendererV2( QgsSymbolV2* symbol )
: QgsFeatureRendererV2( "singleSymbol" )
: QgsFeatureRendererV2( "singleSymbol" ),
mTempSymbol( NULL )
{
mSymbol = symbol;
}
@ -20,22 +25,93 @@ QgsSingleSymbolRendererV2::~QgsSingleSymbolRendererV2()
QgsSymbolV2* QgsSingleSymbolRendererV2::symbolForFeature( QgsFeature& feature )
{
return mSymbol;
if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 )
return mSymbol;
double rotation = 0;
double sizeScale = 1;
if ( mRotationFieldIdx != -1 )
{
rotation = feature.attributeMap()[mRotationFieldIdx].toDouble();
}
if ( mSizeScaleFieldIdx != -1 )
{
sizeScale = feature.attributeMap()[mSizeScaleFieldIdx].toDouble();
}
if ( mTempSymbol->type() == QgsSymbolV2::Marker )
{
QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mTempSymbol );
if ( mRotationFieldIdx != -1 )
markerSymbol->setAngle( rotation );
if ( mSizeScaleFieldIdx != -1 )
markerSymbol->setSize( sizeScale * mOrigSize );
}
else if ( mTempSymbol->type() == QgsSymbolV2::Line )
{
QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( mTempSymbol );
if ( mSizeScaleFieldIdx != -1 )
lineSymbol->setWidth( sizeScale * mOrigSize );
}
return mTempSymbol;
}
void QgsSingleSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
{
mRotationFieldIdx = ( mRotationField.isEmpty() ? -1 : vlayer->fieldNameIndex( mRotationField ) );
mSizeScaleFieldIdx = ( mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField ) );
mSymbol->startRender( context );
if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
{
// we are going to need a temporary symbol
mTempSymbol = mSymbol->clone();
int hints = 0;
if ( mRotationFieldIdx != -1 ) hints |= QgsSymbolV2::DataDefinedRotation;
if ( mSizeScaleFieldIdx != -1 ) hints |= QgsSymbolV2::DataDefinedSizeScale;
mTempSymbol->setRenderHints( hints );
mTempSymbol->startRender( context );
if ( mSymbol->type() == QgsSymbolV2::Marker )
{
mOrigSize = static_cast<QgsMarkerSymbolV2*>( mSymbol )->size();
}
else if ( mSymbol->type() == QgsSymbolV2::Line )
{
mOrigSize = static_cast<QgsLineSymbolV2*>( mSymbol )->width();
}
else
{
mOrigSize = 0;
}
}
}
void QgsSingleSymbolRendererV2::stopRender( QgsRenderContext& context )
{
mSymbol->stopRender( context );
if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
{
// we are going to need a temporary symbol
mTempSymbol->stopRender( context );
delete mTempSymbol;
mTempSymbol = NULL;
}
}
QList<QString> QgsSingleSymbolRendererV2::usedAttributes()
{
return QList<QString>();
QList<QString> lst;
if ( !mRotationField.isEmpty() )
lst.append( mRotationField );
if ( !mSizeScaleField.isEmpty() )
lst.append( mSizeScaleField );
return lst;
}
QgsSymbolV2* QgsSingleSymbolRendererV2::symbol() const
@ -58,6 +134,8 @@ QgsFeatureRendererV2* QgsSingleSymbolRendererV2::clone()
{
QgsSingleSymbolRendererV2* r = new QgsSingleSymbolRendererV2( mSymbol->clone() );
r->setUsingSymbolLevels( usingSymbolLevels() );
r->setRotationField( rotationField() );
r->setSizeScaleField( sizeScaleField() );
return r;
}
@ -84,6 +162,14 @@ QgsFeatureRendererV2* QgsSingleSymbolRendererV2::create( QDomElement& element )
// delete symbols if there are any more
QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap );
QDomElement rotationElem = element.firstChildElement( "rotation" );
if ( !rotationElem.isNull() )
r->setRotationField( rotationElem.attribute( "field" ) );
QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
if ( !sizeScaleElem.isNull() )
r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
// TODO: symbol levels
return r;
}
@ -99,6 +185,14 @@ QDomElement QgsSingleSymbolRendererV2::save( QDomDocument& doc )
QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
rendererElem.appendChild( symbolsElem );
QDomElement rotationElem = doc.createElement( "rotation" );
rotationElem.setAttribute( "field", mRotationField );
rendererElem.appendChild( rotationElem );
QDomElement sizeScaleElem = doc.createElement( "sizescale" );
sizeScaleElem.setAttribute( "field", mSizeScaleField );
rendererElem.appendChild( sizeScaleElem );
return rendererElem;
}

View File

@ -22,6 +22,12 @@ class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2
QgsSymbolV2* symbol() const;
void setSymbol( QgsSymbolV2* s );
void setRotationField( QString fieldName ) { mRotationField = fieldName; }
QString rotationField() const { return mRotationField; }
void setSizeScaleField( QString fieldName ) { mSizeScaleField = fieldName; }
QString sizeScaleField() const { return mSizeScaleField; }
virtual QString dump();
virtual QgsFeatureRendererV2* clone();
@ -43,6 +49,13 @@ class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2
protected:
QgsSymbolV2* mSymbol;
QString mRotationField;
QString mSizeScaleField;
// temporary stuff for rendering
int mRotationFieldIdx, mSizeScaleFieldIdx;
QgsSymbolV2* mTempSymbol;
double mOrigSize;
};

View File

@ -18,7 +18,7 @@
#include <cmath>
QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers )
: mType( type ), mLayers( layers ), mOutputUnit( MM ), mAlpha( 1.0 )
: mType( type ), mLayers( layers ), mOutputUnit( MM ), mAlpha( 1.0 ), mRenderHints( 0 )
{
// check they're all correct symbol layers
@ -127,14 +127,14 @@ bool QgsSymbolV2::changeSymbolLayer( int index, QgsSymbolLayerV2* layer )
void QgsSymbolV2::startRender( QgsRenderContext& context )
{
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha );
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints );
for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
( *it )->startRender( symbolContext );
}
void QgsSymbolV2::stopRender( QgsRenderContext& context )
{
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha );
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints );
for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
( *it )->stopRender( symbolContext );
}
@ -245,8 +245,8 @@ QgsSymbolLayerV2List QgsSymbolV2::cloneLayers() const
////////////////////
QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u, qreal alpha, bool selected )
: mRenderContext( c ), mOutputUnit( u ), mAlpha( alpha ), mSelected( selected )
QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u, qreal alpha, bool selected, int renderHints )
: mRenderContext( c ), mOutputUnit( u ), mAlpha( alpha ), mSelected( selected ), mRenderHints( renderHints )
{
}
@ -347,7 +347,7 @@ double QgsMarkerSymbolV2::size()
void QgsMarkerSymbolV2::renderPoint( const QPointF& point, QgsRenderContext& context, int layer, bool selected )
{
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected );
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints );
if ( layer != -1 )
{
if ( layer >= 0 && layer < mLayers.count() )
@ -416,7 +416,7 @@ double QgsLineSymbolV2::width()
void QgsLineSymbolV2::renderPolyline( const QPolygonF& points, QgsRenderContext& context, int layer, bool selected )
{
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected );
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints );
if ( layer != -1 )
{
if ( layer >= 0 && layer < mLayers.count() )
@ -452,7 +452,7 @@ QgsFillSymbolV2::QgsFillSymbolV2( QgsSymbolLayerV2List layers )
void QgsFillSymbolV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsRenderContext& context, int layer, bool selected )
{
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected );
QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints );
if ( layer != -1 )
{
if ( layer >= 0 && layer < mLayers.count() )

View File

@ -35,6 +35,12 @@ class CORE_EXPORT QgsSymbolV2
Fill
};
enum RenderHint
{
DataDefinedSizeScale = 1,
DataDefinedRotation = 2
};
virtual ~QgsSymbolV2();
//! return new default symbol for specified geometry type
@ -84,6 +90,9 @@ class CORE_EXPORT QgsSymbolV2
qreal alpha() const { return mAlpha; }
void setAlpha( qreal alpha ) { mAlpha = alpha; }
void setRenderHints( int hints ) { mRenderHints = hints; }
int renderHints() { return mRenderHints; }
protected:
QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers ); // can't be instantiated
@ -96,6 +105,8 @@ class CORE_EXPORT QgsSymbolV2
/**Symbol opacity (in the range 0 - 1)*/
qreal mAlpha;
int mRenderHints;
};
///////////////////////
@ -103,7 +114,7 @@ class CORE_EXPORT QgsSymbolV2
class CORE_EXPORT QgsSymbolV2RenderContext
{
public:
QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u , qreal alpha = 1.0, bool selected = false );
QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u , qreal alpha = 1.0, bool selected = false, int renderHints = 0 );
~QgsSymbolV2RenderContext();
QgsRenderContext& renderContext() { return mRenderContext; }
@ -118,6 +129,9 @@ class CORE_EXPORT QgsSymbolV2RenderContext
bool selected() const { return mSelected; }
void setSelected( bool selected ) { mSelected = selected; }
int renderHints() const { return mRenderHints; }
void setRenderHints( int hints ) { mRenderHints = hints; }
// Colour used for selections
static QColor selectionColor();
@ -133,6 +147,7 @@ class CORE_EXPORT QgsSymbolV2RenderContext
QgsSymbolV2::OutputUnit mOutputUnit;
qreal mAlpha;
bool mSelected;
int mRenderHints;
};

View File

@ -3,10 +3,13 @@
#include "qgssinglesymbolrendererv2.h"
#include "qgssymbolv2.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
#include "qgssymbolv2selectordialog.h"
#include <QMenu>
QgsRendererV2Widget* QgsSingleSymbolRendererV2Widget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
{
return new QgsSingleSymbolRendererV2Widget( layer, style, renderer );
@ -43,6 +46,44 @@ QgsSingleSymbolRendererV2Widget::QgsSingleSymbolRendererV2Widget( QgsVectorLayer
layout->addWidget( mSelector );
setLayout( layout );
// advanced actions - data defined rendering
QMenu* advMenu = mSelector->advancedMenu();
mRotationMenu = new QMenu( tr( "Rotation field" ) );
mSizeScaleMenu = new QMenu( tr( "Size scale field" ) );
populateMenu( mRotationMenu, SLOT( rotationFieldSelected() ), mRenderer->rotationField() );
populateMenu( mSizeScaleMenu, SLOT( sizeScaleFieldSelected() ), mRenderer->sizeScaleField() );
advMenu->addMenu( mRotationMenu );
advMenu->addMenu( mSizeScaleMenu );
}
void QgsSingleSymbolRendererV2Widget::populateMenu( QMenu* menu, const char* slot, QString fieldName )
{
QAction* aNo = menu->addAction( tr( "- no field -" ), this, slot );
aNo->setCheckable( true );
menu->addSeparator();
bool hasField = false;
const QgsFieldMap& flds = mLayer->pendingFields();
for ( QgsFieldMap::const_iterator it = flds.begin(); it != flds.end(); ++it )
{
const QgsField& fld = it.value();
if ( fld.type() == QVariant::Int || fld.type() == QVariant::Double )
{
QAction* a = menu->addAction( fld.name(), this, slot );
a->setCheckable( true );
if ( fieldName == fld.name() )
{
a->setChecked( true );
hasField = true;
}
}
}
if ( !hasField )
aNo->setChecked( true );
}
QgsSingleSymbolRendererV2Widget::~QgsSingleSymbolRendererV2Widget()
@ -66,3 +107,50 @@ void QgsSingleSymbolRendererV2Widget::changeSingleSymbol()
mRenderer->setSymbol( mSingleSymbol->clone() );
}
void QgsSingleSymbolRendererV2Widget::rotationFieldSelected()
{
QObject* s = sender();
if ( s == NULL )
return;
QAction* a = qobject_cast<QAction*>( s );
if ( a == NULL )
return;
QString fldName = a->text();
updateMenu( mRotationMenu, fldName );
if ( fldName == tr( "- no field -" ) )
fldName = QString();
mRenderer->setRotationField( fldName );
}
void QgsSingleSymbolRendererV2Widget::sizeScaleFieldSelected()
{
QObject* s = sender();
if ( s == NULL )
return;
QAction* a = qobject_cast<QAction*>( s );
if ( a == NULL )
return;
QString fldName = a->text();
updateMenu( mSizeScaleMenu, fldName );
if ( fldName == tr( "- no field -" ) )
fldName = QString();
mRenderer->setSizeScaleField( fldName );
}
void QgsSingleSymbolRendererV2Widget::updateMenu( QMenu* menu, QString fieldName )
{
foreach( QAction* a, menu->actions() )
{
a->setChecked( a->text() == fieldName );
}
}

View File

@ -6,6 +6,8 @@
class QgsSingleSymbolRendererV2;
class QgsSymbolV2SelectorDialog;
class QMenu;
class GUI_EXPORT QgsSingleSymbolRendererV2Widget : public QgsRendererV2Widget
{
Q_OBJECT
@ -22,10 +24,20 @@ class GUI_EXPORT QgsSingleSymbolRendererV2Widget : public QgsRendererV2Widget
public slots:
void changeSingleSymbol();
void rotationFieldSelected();
void sizeScaleFieldSelected();
protected:
void populateMenu( QMenu* menu, const char* slot, QString fieldName );
void updateMenu( QMenu* menu, QString fieldName );
QgsSingleSymbolRendererV2* mRenderer;
QgsSymbolV2SelectorDialog* mSelector;
QgsSymbolV2* mSingleSymbol;
QMenu* mRotationMenu;
QMenu* mSizeScaleMenu;
};

View File

@ -14,15 +14,18 @@
#include <QStandardItemModel>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMenu>
QgsSymbolV2SelectorDialog::QgsSymbolV2SelectorDialog( QgsSymbolV2* symbol, QgsStyleV2* style, QWidget* parent, bool embedded )
: QDialog( parent )
: QDialog( parent ), mAdvancedMenu( NULL )
{
mStyle = style;
mSymbol = symbol;
setupUi( this );
btnAdvanced->hide(); // advanced button is hidden by default
// can be embedded in renderer properties dialog
if ( embedded )
{
@ -262,3 +265,14 @@ void QgsSymbolV2SelectorDialog::displayTransparency( double alpha )
double transparencyPercent = ( 1 - alpha ) * 100;
mTransparencyLabel->setText( tr( "Transparency: %1%" ).arg(( int ) transparencyPercent ) );
}
QMenu* QgsSymbolV2SelectorDialog::advancedMenu()
{
if ( mAdvancedMenu == NULL )
{
mAdvancedMenu = new QMenu;
btnAdvanced->setMenu( mAdvancedMenu );
btnAdvanced->show();
}
return mAdvancedMenu;
}

View File

@ -9,6 +9,8 @@
class QgsStyleV2;
class QgsSymbolV2;
class QMenu;
class GUI_EXPORT QgsSymbolV2SelectorDialog : public QDialog, private Ui::QgsSymbolV2SelectorDialogBase
{
Q_OBJECT
@ -16,6 +18,9 @@ class GUI_EXPORT QgsSymbolV2SelectorDialog : public QDialog, private Ui::QgsSymb
public:
QgsSymbolV2SelectorDialog( QgsSymbolV2* symbol, QgsStyleV2* style, QWidget* parent = NULL, bool embedded = false );
//! return menu for "advanced" button - create it if doesn't exist and show the advanced button
QMenu* advancedMenu();
protected:
void populateSymbolView();
void updateSymbolPreview();
@ -46,6 +51,7 @@ class GUI_EXPORT QgsSymbolV2SelectorDialog : public QDialog, private Ui::QgsSymb
protected:
QgsStyleV2* mStyle;
QgsSymbolV2* mSymbol;
QMenu* mAdvancedMenu;
};
#endif

View File

@ -29,7 +29,7 @@
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
@ -221,6 +221,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnAdvanced">
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnAddToStyle">
<property name="text">