[QGIS Commit] r13389 - in trunk/qgis: python/core src/core/symbology-ng src/gui/symbology-ng src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Apr 25 18:32:53 EDT 2010


Author: wonder
Date: 2010-04-25 18:32:53 -0400 (Sun, 25 Apr 2010)
New Revision: 13389

Modified:
   trunk/qgis/python/core/symbology-ng-core.sip
   trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
   trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.h
   trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
   trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.h
   trunk/qgis/src/core/symbology-ng/qgssymbolv2.cpp
   trunk/qgis/src/core/symbology-ng/qgssymbolv2.h
   trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.cpp
   trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.h
   trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.cpp
   trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.h
   trunk/qgis/src/ui/qgssymbolv2selectordialogbase.ui
Log:
[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)



Modified: trunk/qgis/python/core/symbology-ng-core.sip
===================================================================
--- trunk/qgis/python/core/symbology-ng-core.sip	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/python/core/symbology-ng-core.sip	2010-04-25 22:32:53 UTC (rev 13389)
@@ -133,6 +133,12 @@
   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 @@
 %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 @@
     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 @@
   	Fill
   };
 
+    enum RenderHint
+    {
+      DataDefinedSizeScale = 1,
+      DataDefinedRotation = 2
+    };
+
   virtual ~QgsSymbolV2();
 
   //! return new default symbol for specified geometry type
@@ -556,6 +573,12 @@
     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
 

Modified: trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp	2010-04-25 22:32:53 UTC (rev 13389)
@@ -68,102 +68,72 @@
   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" )
+
+  QMatrix transform;
+
+  // scale the shape (if the size is not going to be modified)
+  if ( !hasDataDefinedSize )
   {
-    mPolygon << QPointF( -half, 0 ) << QPointF( 0, half )
-    << QPointF( half, 0 ) << QPointF( 0, -half );
+    double scaledSize = context.outputPixelSize( mSize );
+    double half = scaledSize / 2.0;
+    transform.scale( half, half );
   }
-  else if ( mName == "pentagon" )
+
+  // rotate if the rotation is not going to be changed during the rendering
+  if ( !hasDataDefinedRotation && mAngle != 0 )
   {
-    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 );
+    transform.rotate( mAngle );
   }
-  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 ) );
+  if ( !mPolygon.isEmpty() )
+    mPolygon = transform.map( mPolygon );
+  else
+    mPath = transform.map( mPath );
 
-    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" )
+  if ( !hasDataDefinedRotation && !hasDataDefinedSize )
   {
-    double eight = half / 4;
-    double quarter = half / 2;
-
-    mPolygon << QPointF( 0,        -half )
-    << QPointF( quarter,  -quarter )
-    << QPointF( eight,    -quarter )
-    << QPointF( eight,      half )
-    << QPointF( -eight,     half )
-    << QPointF( -eight,    -quarter )
-    << QPointF( -quarter,  -quarter );
+    // 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 @@
 
   // 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 @@
 {
 }
 
+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 @@
     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 @@
   }
   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 @@
   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 @@
   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 @@
   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 @@
 
   p->save();
   p->translate( point );
+
+  if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
+  {
+    double s = mSize / mOrigSize;
+    p->scale( s, s );
+  }
+
   if ( mAngle != 0 )
     p->rotate( mAngle );
 

Modified: trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.h
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.h	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/core/symbology-ng/qgsmarkersymbollayerv2.h	2010-04-25 22:32:53 UTC (rev 13389)
@@ -53,10 +53,16 @@
 
     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 @@
     QString mPath;
     QPicture mPicture;
     QPicture mSelPicture;
+    double mOrigSize;
 };
 
 
@@ -170,6 +177,7 @@
 
     QPointF mChrOffset;
     QFont mFont;
+    double mOrigSize;
 };
 
 

Modified: trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp	2010-04-25 22:32:53 UTC (rev 13389)
@@ -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 @@
 
 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 @@
 {
   QgsSingleSymbolRendererV2* r = new QgsSingleSymbolRendererV2( mSymbol->clone() );
   r->setUsingSymbolLevels( usingSymbolLevels() );
+  r->setRotationField( rotationField() );
+  r->setSizeScaleField( sizeScaleField() );
   return r;
 }
 
@@ -84,6 +162,14 @@
   // 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 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;
 }
 

Modified: trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.h
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.h	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.h	2010-04-25 22:32:53 UTC (rev 13389)
@@ -22,6 +22,12 @@
     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 @@
 
   protected:
     QgsSymbolV2* mSymbol;
+    QString mRotationField;
+    QString mSizeScaleField;
+
+    // temporary stuff for rendering
+    int mRotationFieldIdx, mSizeScaleFieldIdx;
+    QgsSymbolV2* mTempSymbol;
+    double mOrigSize;
 };
 
 

Modified: trunk/qgis/src/core/symbology-ng/qgssymbolv2.cpp
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgssymbolv2.cpp	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/core/symbology-ng/qgssymbolv2.cpp	2010-04-25 22:32:53 UTC (rev 13389)
@@ -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 @@
 
 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 @@
 
 ////////////////////
 
-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 @@
 
 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 @@
 
 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 @@
 
 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() )

Modified: trunk/qgis/src/core/symbology-ng/qgssymbolv2.h
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgssymbolv2.h	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/core/symbology-ng/qgssymbolv2.h	2010-04-25 22:32:53 UTC (rev 13389)
@@ -35,6 +35,12 @@
       Fill
     };
 
+    enum RenderHint
+    {
+      DataDefinedSizeScale = 1,
+      DataDefinedRotation = 2
+    };
+
     virtual ~QgsSymbolV2();
 
     //! return new default symbol for specified geometry type
@@ -84,6 +90,9 @@
     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 @@
 
     /**Symbol opacity (in the range 0 - 1)*/
     qreal mAlpha;
+
+    int mRenderHints;
 };
 
 ///////////////////////
@@ -103,7 +114,7 @@
 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 @@
     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 @@
     QgsSymbolV2::OutputUnit mOutputUnit;
     qreal mAlpha;
     bool mSelected;
+    int mRenderHints;
 };
 
 

Modified: trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.cpp
===================================================================
--- trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.cpp	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.cpp	2010-04-25 22:32:53 UTC (rev 13389)
@@ -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,8 +46,46 @@
   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()
 {
   delete mSingleSymbol;
@@ -66,3 +107,50 @@
   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 );
+  }
+}

Modified: trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.h
===================================================================
--- trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.h	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/gui/symbology-ng/qgssinglesymbolrendererv2widget.h	2010-04-25 22:32:53 UTC (rev 13389)
@@ -6,6 +6,8 @@
 class QgsSingleSymbolRendererV2;
 class QgsSymbolV2SelectorDialog;
 
+class QMenu;
+
 class GUI_EXPORT QgsSingleSymbolRendererV2Widget : public QgsRendererV2Widget
 {
     Q_OBJECT
@@ -22,10 +24,20 @@
   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;
 };
 
 

Modified: trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.cpp
===================================================================
--- trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.cpp	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.cpp	2010-04-25 22:32:53 UTC (rev 13389)
@@ -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 @@
   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;
+}

Modified: trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.h
===================================================================
--- trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.h	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/gui/symbology-ng/qgssymbolv2selectordialog.h	2010-04-25 22:32:53 UTC (rev 13389)
@@ -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 @@
   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 @@
   protected:
     QgsStyleV2* mStyle;
     QgsSymbolV2* mSymbol;
+    QMenu* mAdvancedMenu;
 };
 
 #endif

Modified: trunk/qgis/src/ui/qgssymbolv2selectordialogbase.ui
===================================================================
--- trunk/qgis/src/ui/qgssymbolv2selectordialogbase.ui	2010-04-25 22:12:05 UTC (rev 13388)
+++ trunk/qgis/src/ui/qgssymbolv2selectordialogbase.ui	2010-04-25 22:32:53 UTC (rev 13389)
@@ -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>
@@ -222,6 +222,13 @@
       </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">
         <string>Add to style</string>



More information about the QGIS-commit mailing list