[QGIS Commit] r13139 - in trunk/qgis/src/plugins: . point_displacement_renderer

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Mar 21 18:14:14 EDT 2010


Author: mhugent
Date: 2010-03-21 18:14:14 -0400 (Sun, 21 Mar 2010)
New Revision: 13139

Added:
   trunk/qgis/src/plugins/point_displacement_renderer/
   trunk/qgis/src/plugins/point_displacement_renderer/CMakeLists.txt
   trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.cpp
   trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.h
   trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.cpp
   trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.h
   trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.cpp
   trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.h
   trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidgetbase.ui
Modified:
   trunk/qgis/src/plugins/CMakeLists.txt
Log:
[FEATURE]: Add point displacement renderer plugin

Modified: trunk/qgis/src/plugins/CMakeLists.txt
===================================================================
--- trunk/qgis/src/plugins/CMakeLists.txt	2010-03-21 22:06:37 UTC (rev 13138)
+++ trunk/qgis/src/plugins/CMakeLists.txt	2010-03-21 22:14:14 UTC (rev 13139)
@@ -12,6 +12,7 @@
   diagram_overlay
   evis
   labeling
+  point_displacement_renderer
   )
 
 IF (POSTGRES_FOUND)

Added: trunk/qgis/src/plugins/point_displacement_renderer/CMakeLists.txt
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/CMakeLists.txt	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/CMakeLists.txt	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,55 @@
+########################################################
+# Files
+
+SET (POINT_DISPLACEMENT_SRCS
+     qgsdisplacementplugin.cpp
+     qgspointdisplacementrenderer.cpp
+     qgspointdisplacementrendererwidget.cpp
+)
+
+SET (POINT_DISPLACEMENT_UIS 
+    qgspointdisplacementrendererwidgetbase.ui
+    )
+
+SET (POINT_DISPLACEMENT_MOC_HDRS
+  qgspointdisplacementrendererwidget.h
+)
+
+########################################################
+# Build
+
+QT4_WRAP_UI (POINT_DISPLACEMENT_UIS_H  ${POINT_DISPLACEMENT_UIS})
+
+QT4_WRAP_CPP (POINT_DISPLACEMENT_MOC_SRCS  ${POINT_DISPLACEMENT_MOC_HDRS})
+
+ADD_LIBRARY (displacementplugin MODULE 
+  ${POINT_DISPLACEMENT_SRCS}
+${POINT_DISPLACEMENT_UIS_H}
+${POINT_DISPLACEMENT_MOC_SRCS}
+)
+
+INCLUDE_DIRECTORIES(
+     ${CMAKE_CURRENT_BINARY_DIR}
+    ${CMAKE_CURRENT_BINARY_DIR}/../../ui
+     ../../core 
+     ../../core/symbology-ng
+     ../../core/spatialindex
+    ../../gui
+    ../../gui/symbology-ng
+     ..
+     .
+)
+
+TARGET_LINK_LIBRARIES(displacementplugin
+  qgis_core
+  qgis_gui
+)
+
+
+########################################################
+# Install
+
+INSTALL(TARGETS displacementplugin
+  RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
+  LIBRARY DESTINATION ${QGIS_PLUGIN_DIR}
+  )

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.cpp
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.cpp	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.cpp	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,86 @@
+/***************************************************************************
+                              qgsdisplacementplugin.cpp
+                              -------------------------
+  begin                : January 26, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco at hugis dot net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "qgsdisplacementplugin.h"
+#include "qgisinterface.h"
+#include "qgspointdisplacementrenderer.h"
+#include "qgspointdisplacementrendererwidget.h"
+#include "qgsrendererv2registry.h"
+#include "qgssymbollayerv2registry.h"
+#include <QObject>
+
+static const QString name_ = QObject::tr( "Displacement plugin" );
+static const QString description_ = QObject::tr( "Adds a new renderer that automatically handles point displacement in case they have the same position" );
+static const QString version_ = QObject::tr( "Version 0.1" );
+
+QgsDisplacementPlugin::QgsDisplacementPlugin( QgisInterface* iface ): mIface( iface )
+{
+
+}
+
+QgsDisplacementPlugin::~QgsDisplacementPlugin()
+{
+
+}
+
+void QgsDisplacementPlugin::initGui()
+{
+  //Add new renderer to the registry
+
+  QgsRendererV2Registry::instance()->addRenderer( new QgsRendererV2Metadata( "pointDisplacement",
+      QObject::tr( "point Displacement" ),
+      QgsPointDisplacementRenderer::create, QIcon(),
+      QgsPointDisplacementRendererWidget::create ) );
+}
+
+void QgsDisplacementPlugin::unload()
+{
+  //Remove renderer type from the registry
+  QgsRendererV2Registry::instance()->removeRenderer( "pointDisplacement" );
+}
+
+QGISEXTERN QgisPlugin * classFactory( QgisInterface * theQgisInterfacePointer )
+{
+  return new QgsDisplacementPlugin( theQgisInterfacePointer );
+}
+
+QGISEXTERN QString name()
+{
+  return name_;
+}
+
+QGISEXTERN QString description()
+{
+  return description_;
+}
+
+QGISEXTERN QString version()
+{
+  return version_;
+}
+
+QGISEXTERN int type()
+{
+  return QgisPlugin::UI;
+}
+
+QGISEXTERN void unload( QgisPlugin* thePluginPointer )
+{
+  delete thePluginPointer;
+}
+
+

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.h
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.h	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgsdisplacementplugin.h	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,40 @@
+/***************************************************************************
+                              qgsdisplacementplugin.h
+                              -----------------------
+  begin                : January 26, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco at hugis dot net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef QGSDISPLACEMENTPLUGIN_H
+#define QGSDISPLACEMENTPLUGIN_H
+
+#include "qgisplugin.h"
+
+class QgisInterface;
+
+/**A plugin that adds a point displacement renderer to the symbol registry*/
+class QgsDisplacementPlugin: public QgisPlugin
+{
+  public:
+    QgsDisplacementPlugin( QgisInterface* iface );
+    ~QgsDisplacementPlugin();
+    /**Adds renderer to the registry*/
+    void initGui();
+    /**Removes renderer from the registry*/
+    void unload();
+
+  private:
+    QgisInterface* mIface;
+};
+
+#endif // QGSDISPLACEMENTPLUGIN_H

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.cpp
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.cpp	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.cpp	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,557 @@
+/***************************************************************************
+                              qgspointdisplacementrenderer.cpp
+                              --------------------------------
+  begin                : January 26, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco at hugis dot net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "qgspointdisplacementrenderer.h"
+#include "qgsgeometry.h"
+#include "qgslogger.h"
+#include "qgsrendererv2registry.h"
+#include "qgsspatialindex.h"
+#include "qgssymbolv2.h"
+#include "qgssymbollayerv2utils.h"
+#include "qgsvectorlayer.h"
+#include <QDomElement>
+#include <QPainter>
+
+#ifndef Q_OS_MACX
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString& labelAttributeName ): QgsFeatureRendererV2( "pointDisplacement" ), \
+    mLabelAttributeName( labelAttributeName ), mLabelIndex( -1 ), mTolerance( 0.00001 ), mCircleWidth( 0.4 ), mCircleColor( QColor( 125, 125, 125 ) ), mCircleRadiusAddition( 0 ), \
+    mMaxLabelScaleDenominator( -1 )
+{
+  mRenderer = QgsFeatureRendererV2::defaultRenderer( QGis::Point );
+  mCenterSymbol = new QgsMarkerSymbolV2(); //the symbol for the center of a displacement group
+  mDrawLabels = true;
+}
+
+QgsPointDisplacementRenderer::~QgsPointDisplacementRenderer()
+{
+  delete mCenterSymbol;
+  delete mRenderer;
+}
+
+QgsFeatureRendererV2* QgsPointDisplacementRenderer::clone()
+{
+  QgsPointDisplacementRenderer* r = new QgsPointDisplacementRenderer( mLabelAttributeName );
+  r->setEmbeddedRenderer( mRenderer->clone() );
+  r->setDisplacementGroups( mDisplacementGroups );
+  r->setCircleWidth( mCircleWidth );
+  r->setCircleColor( mCircleColor );
+  r->setLabelFont( mLabelFont );
+  r->setLabelColor( mLabelColor );
+  r->setCircleRadiusAddition( mCircleRadiusAddition );
+  r->setMaxLabelScaleDenominator( mMaxLabelScaleDenominator );
+  if ( mCenterSymbol )
+  {
+    r->setCenterSymbol( dynamic_cast<QgsMarkerSymbolV2*>( mCenterSymbol->clone() ) );
+  }
+  return r;
+}
+
+void QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool drawVertexMarker )
+{
+  //point position in screen coords
+  QgsGeometry* geom = feature.geometry();
+  QGis::WkbType geomType = geom->wkbType();
+  if ( geomType != QGis::WKBPoint && geomType != QGis::WKBPoint25D )
+  {
+    //can only render point type
+    return;
+  }
+  QPointF pt;
+  _getPoint( pt, context, geom->asWkb() );
+
+
+  //get list of labels and symbols
+  QStringList labelAttributeList;
+  QList<QgsMarkerSymbolV2*> symbolList;
+
+  if ( mDisplacementIds.contains( feature.id() ) )
+  {
+    //create the symbol for the whole display group if the id is the first entry in a display group
+    QList<QMap<int, QgsFeature> >::iterator it = mDisplacementGroups.begin();
+    for ( ; it != mDisplacementGroups.end(); ++it )
+    {
+      //create the symbol for the whole display group if the id is the first entry in a display group
+      if ( feature.id() == it->begin().key() )
+      {
+        QMap<int, QgsFeature>::iterator attIt = it->begin();
+        for ( ; attIt != it->end(); ++attIt )
+        {
+          if ( mDrawLabels )
+          {
+            labelAttributeList << getLabel( attIt.value() );
+          }
+          else
+          {
+            labelAttributeList << QString();
+          }
+          symbolList << dynamic_cast<QgsMarkerSymbolV2*>( mRenderer->symbolForFeature( attIt.value() ) );
+        }
+      }
+    }
+  }
+  else //only one feature
+  {
+    symbolList << dynamic_cast<QgsMarkerSymbolV2*>( mRenderer->symbolForFeature( feature ) );
+    if ( mDrawLabels )
+    {
+      labelAttributeList << getLabel( feature );
+    }
+    else
+    {
+      labelAttributeList << QString();
+    }
+  }
+
+  if ( symbolList.isEmpty() && labelAttributeList.isEmpty() )
+  {
+    return; //display all point symbols for one posi
+  }
+
+
+  //draw symbol
+  double diagonal = 0;
+  double currentWidthFactor; //scale symbol size to map unit and output resolution
+
+  QList<QgsMarkerSymbolV2*>::const_iterator it = symbolList.constBegin();
+  for ( ; it != symbolList.constEnd(); ++it )
+  {
+    if ( *it )
+    {
+      currentWidthFactor = QgsSymbolLayerV2Utils::lineWidthScaleFactor( context, ( *it )->outputUnit() );
+      double currentDiagonal = sqrt( 2 * (( *it )->size() * ( *it )->size() ) ) * currentWidthFactor;
+      if ( currentDiagonal > diagonal )
+      {
+        diagonal = currentDiagonal;
+      }
+    }
+  }
+
+
+  QgsSymbolV2RenderContext symbolContext( context, QgsSymbolV2::MM );
+  double circleAdditionPainterUnits = symbolContext.outputLineWidth( mCircleRadiusAddition );
+  double radius = std::max(( diagonal / 2 ), labelAttributeList.size() * diagonal / 2 / M_PI ) + circleAdditionPainterUnits;
+
+  //draw Circle
+  drawCircle( radius, symbolContext, pt, symbolList.size() );
+
+  QList<QPointF> symbolPositions;
+  QList<QPointF> labelPositions;
+  calculateSymbolAndLabelPositions( pt, labelAttributeList.size(), radius, diagonal, symbolPositions, labelPositions );
+
+  //draw mid point
+  if ( labelAttributeList.size() > 1 )
+  {
+    if ( mCenterSymbol )
+    {
+      mCenterSymbol->renderPoint( pt, context );
+    }
+    else
+    {
+      context.painter()->drawRect( QRectF( pt.x() - symbolContext.outputLineWidth( 1 ), pt.y() - symbolContext.outputLineWidth( 1 ), symbolContext.outputLineWidth( 2 ), symbolContext.outputLineWidth( 2 ) ) );
+    }
+  }
+
+  //draw symbols on the circle
+  drawSymbols( context, symbolList, symbolPositions );
+  //and also the labels
+  drawLabels( pt, symbolContext, labelPositions, labelAttributeList );
+}
+
+void QgsPointDisplacementRenderer::setEmbeddedRenderer( QgsFeatureRendererV2* r )
+{
+  delete mRenderer;
+  mRenderer = r;
+}
+
+QgsSymbolV2* QgsPointDisplacementRenderer::symbolForFeature( QgsFeature& feature )
+{
+  return 0; //not used any more
+}
+
+void QgsPointDisplacementRenderer::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
+{
+  mRenderer->startRender( context, vlayer );
+
+  //create groups with features that have the same position
+  createDisplacementGroups( const_cast<QgsVectorLayer*>( vlayer ), context.extent() );
+  printInfoDisplacementGroups(); //just for debugging
+
+  if ( mLabelAttributeName.isEmpty() )
+  {
+    mLabelIndex = -1;
+  }
+  else
+  {
+    mLabelIndex = vlayer->fieldNameIndex( mLabelAttributeName );
+  }
+
+  if ( mMaxLabelScaleDenominator > 0 && context.rendererScale() > mMaxLabelScaleDenominator )
+  {
+    mDrawLabels = false;
+  }
+  else
+  {
+    mDrawLabels = true;
+  }
+
+  if ( mCenterSymbol )
+  {
+    mCenterSymbol->startRender( context );
+  }
+}
+
+void QgsPointDisplacementRenderer::stopRender( QgsRenderContext& context )
+{
+  QgsDebugMsg( "QgsPointDisplacementRenderer::stopRender" );
+  mRenderer->stopRender( context );
+  if ( mCenterSymbol )
+  {
+    mCenterSymbol->stopRender( context );
+  }
+}
+
+QList<QString> QgsPointDisplacementRenderer::usedAttributes()
+{
+  QList<QString> attributeList;
+  if ( !mLabelAttributeName.isEmpty() )
+  {
+    attributeList.push_back( mLabelAttributeName );
+  }
+  if ( mRenderer )
+  {
+    attributeList.append( mRenderer->usedAttributes() );
+  }
+  return attributeList;
+}
+
+QgsSymbolV2List QgsPointDisplacementRenderer::symbols()
+{
+  if ( mRenderer )
+  {
+    return mRenderer->symbols();
+  }
+  else
+  {
+    return QgsSymbolV2List();
+  }
+}
+
+QgsFeatureRendererV2* QgsPointDisplacementRenderer::create( QDomElement& symbologyElem )
+{
+  QgsPointDisplacementRenderer* r = new QgsPointDisplacementRenderer();
+  r->setLabelAttributeName( symbologyElem.attribute( "labelAttributeName" ) );
+  QFont labelFont;
+  labelFont.fromString( symbologyElem.attribute( "labelFont", "" ) );
+  r->setLabelFont( labelFont );
+  r->setCircleWidth( symbologyElem.attribute( "circleWidth", "0.4" ).toDouble() );
+  r->setCircleColor( QgsSymbolLayerV2Utils::decodeColor( symbologyElem.attribute( "circleColor", "" ) ) );
+  r->setLabelColor( QgsSymbolLayerV2Utils::decodeColor( symbologyElem.attribute( "labelColor", "" ) ) );
+  r->setCircleRadiusAddition( symbologyElem.attribute( "circleRadiusAddition", "0.0" ).toDouble() );
+  r->setMaxLabelScaleDenominator( symbologyElem.attribute( "maxLabelScaleDenominator", "-1" ).toDouble() );
+
+  //look for an embedded renderer <renderer-v2>
+  QDomElement embeddedRendererElem = symbologyElem.firstChildElement( "renderer-v2" );
+  if ( !embeddedRendererElem.isNull() )
+  {
+    QString rendererName = embeddedRendererElem.attribute( "type" );
+    QgsRendererV2AbstractMetadata* metaData = QgsRendererV2Registry::instance()->rendererMetadata( rendererName );
+    if ( metaData )
+    {
+      r->setEmbeddedRenderer( metaData->createRenderer( embeddedRendererElem ) );
+    }
+  }
+
+  //center symbol
+  QDomElement centerSymbolElem = symbologyElem.firstChildElement( "symbol" );
+  if ( !centerSymbolElem.isNull() )
+  {
+    r->setCenterSymbol( dynamic_cast<QgsMarkerSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( centerSymbolElem ) ) );
+  }
+  return r;
+}
+
+QDomElement QgsPointDisplacementRenderer::save( QDomDocument& doc )
+{
+  QDomElement rendererElement = doc.createElement( RENDERER_TAG_NAME );
+  rendererElement.setAttribute( "type", "pointDisplacement" );
+  rendererElement.setAttribute( "labelAttributeName", mLabelAttributeName );
+  rendererElement.setAttribute( "labelFont", mLabelFont.toString() );
+  rendererElement.setAttribute( "circleWidth", mCircleWidth );
+  rendererElement.setAttribute( "circleColor", QgsSymbolLayerV2Utils::encodeColor( mCircleColor ) );
+  rendererElement.setAttribute( "labelColor", QgsSymbolLayerV2Utils::encodeColor( mLabelColor ) );
+  rendererElement.setAttribute( "circleRadiusAddition", mCircleRadiusAddition );
+  rendererElement.setAttribute( "maxLabelScaleDenominator", mMaxLabelScaleDenominator );
+
+  if ( mRenderer )
+  {
+    QDomElement embeddedRendererElem = mRenderer->save( doc );
+    rendererElement.appendChild( embeddedRendererElem );
+  }
+  if ( mCenterSymbol )
+  {
+    QDomElement centerSymbolElem = QgsSymbolLayerV2Utils::saveSymbol( "centerSymbol", mCenterSymbol, doc );
+    rendererElement.appendChild( centerSymbolElem );
+  }
+  return rendererElement;
+}
+
+QgsLegendSymbologyList QgsPointDisplacementRenderer::legendSymbologyItems( QSize iconSize )
+{
+  if ( mRenderer )
+  {
+    return mRenderer->legendSymbologyItems( iconSize );
+  }
+  return QgsLegendSymbologyList();
+}
+
+void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent )
+{
+  if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) )
+  {
+    return;
+  }
+
+  mDisplacementGroups.clear();
+  mDisplacementIds.clear();
+
+  //use a spatial index to check if there is already a point at a position
+  QgsSpatialIndex spatialIndex;
+
+  //attributes
+  QgsAttributeList attList;
+  QList<QString> attributeStrings = usedAttributes();
+  QList<QString>::const_iterator attStringIt = attributeStrings.constBegin();
+  for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt )
+  {
+    attList.push_back( vlayer->fieldNameIndex( *attStringIt ) );
+  }
+
+  QgsFeature f;
+  QList<int> intersectList;
+
+  vlayer->select( attList, viewExtent, true, false );
+  while ( vlayer->nextFeature( f ) )
+  {
+    intersectList.clear();
+
+    //check, if there is already a point at that position
+    if ( f.geometry() )
+    {
+      intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) );
+      if ( intersectList.empty() )
+      {
+        spatialIndex.insertFeature( f );
+      }
+      else
+      {
+        //go through all the displacement group maps and search an entry where the id equals the result of the spatial search
+        int existingEntry = intersectList.at( 0 );
+        bool found = false;
+        QList<QMap<int, QgsFeature> >::iterator it = mDisplacementGroups.begin();
+        for ( ; it != mDisplacementGroups.end(); ++it )
+        {
+          if ( it->size() > 0 && it->contains( existingEntry ) )
+          {
+            found = true;
+            QgsFeature feature;
+            it->insert( f.id(), f );
+            mDisplacementIds.insert( f.id() );
+            break;
+          }
+        }
+
+        if ( !found )//insert the already existing feature and the new one into a map
+        {
+          QMap<int, QgsFeature> newMap;
+          QgsFeature existingFeature;
+          vlayer->featureAtId( existingEntry, existingFeature );
+          newMap.insert( existingEntry, existingFeature );
+          mDisplacementIds.insert( existingEntry );
+          newMap.insert( f.id(), f );
+          mDisplacementIds.insert( f.id() );
+          mDisplacementGroups.push_back( newMap );
+        }
+      }
+    }
+  }
+  //refresh the selection because the vector layer is going to step through all features now
+  vlayer->select( attList, viewExtent, true, false );
+}
+
+QgsRectangle QgsPointDisplacementRenderer::searchRect( const QgsPoint& p ) const
+{
+  return QgsRectangle( p.x() - mTolerance, p.y() - mTolerance, p.x() + mTolerance, p.y() + mTolerance );
+}
+
+void QgsPointDisplacementRenderer::printInfoDisplacementGroups()
+{
+  int nGroups = mDisplacementGroups.size();
+  QgsDebugMsg( "number of displacement groups:" + QString::number( nGroups ) );
+  for ( int i = 0; i < nGroups; ++i )
+  {
+    QgsDebugMsg( "***************displacement group " + QString::number( i ) );
+    QMap<int, QgsFeature>::const_iterator it = mDisplacementGroups.at( i ).constBegin();
+    for ( ; it != mDisplacementGroups.at( i ).constEnd(); ++it )
+    {
+      QgsDebugMsg( QString::number( it.key() ) );
+    }
+  }
+  QgsDebugMsg( "********all displacement ids*********" );
+  QSet<int>::const_iterator iIt = mDisplacementIds.constBegin();
+  for ( ; iIt != mDisplacementIds.constEnd(); ++iIt )
+  {
+    QgsDebugMsg( QString::number( *iIt ) );
+  }
+}
+
+void QgsPointDisplacementRenderer::setDisplacementGroups( const QList<QMap<int, QgsFeature> >& list )
+{
+  mDisplacementGroups = list;
+  mDisplacementIds.clear();
+
+  QList<QMap<int, QgsFeature> >::const_iterator list_it = mDisplacementGroups.constBegin();
+  for ( ; list_it != mDisplacementGroups.constEnd(); ++list_it )
+  {
+    QMap<int, QgsFeature>::const_iterator map_it = list_it->constBegin();
+    for ( ; map_it != list_it->constEnd(); ++map_it )
+    {
+      mDisplacementIds.insert( map_it.key() );
+    }
+  }
+}
+
+QString QgsPointDisplacementRenderer::getLabel( const QgsFeature& f )
+{
+  QString attribute;
+  QgsAttributeMap attMap = f.attributeMap();
+  if ( attMap.size() > 0 )
+  {
+    QgsAttributeMap::const_iterator valIt = attMap.find( mLabelIndex );
+    if ( valIt != attMap.constEnd() )
+    {
+      attribute = valIt->toString();
+    }
+  }
+  return attribute;
+}
+
+void QgsPointDisplacementRenderer::setCenterSymbol( QgsMarkerSymbolV2* symbol )
+{
+  delete mCenterSymbol;
+  mCenterSymbol = symbol;
+}
+
+
+
+void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius, \
+    double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const
+{
+  symbolPositions.clear();
+  labelShifts.clear();
+
+  double fullPerimeter = 2 * M_PI;
+  double angleStep = fullPerimeter / nPosition;
+  double currentAngle;
+
+  for ( currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
+  {
+    double sinusCurrentAngle = sin( currentAngle );
+    double cosinusCurrentAngle = cos( currentAngle );
+    QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
+    QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
+    symbolPositions.append( centerPoint + positionShift );
+    labelShifts.append( labelShift );
+  }
+}
+
+void QgsPointDisplacementRenderer::drawCircle( double radiusPainterUnits, QgsSymbolV2RenderContext& context, const QPointF& centerPoint, int nSymbols )
+{
+  QPainter* p = context.renderContext().painter();
+  if ( nSymbols < 2 || !p ) //draw circle only if multiple features
+  {
+    return;
+  }
+
+  //draw Circle
+  QPen circlePen( mCircleColor );
+  circlePen.setWidthF( context.outputLineWidth( mCircleWidth ) );
+  p->setPen( circlePen );
+  p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
+}
+
+void QgsPointDisplacementRenderer::drawSymbols( QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions )
+{
+  QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
+  QList<QgsMarkerSymbolV2*>::const_iterator symbolIt = symbolList.constBegin();
+  for ( ; symbolPosIt != symbolPositions.constEnd() && symbolIt != symbolList.constEnd(); ++symbolPosIt, ++symbolIt )
+  {
+    if ( *symbolIt )
+    {
+      ( *symbolIt )->renderPoint( *symbolPosIt, context );
+    }
+  }
+}
+
+void QgsPointDisplacementRenderer::drawLabels( const QPointF& centerPoint, QgsSymbolV2RenderContext& context, const QList<QPointF>& labelShifts, const QStringList& labelList )
+{
+  QPainter* p = context.renderContext().painter();
+  if ( !p )
+  {
+    return;
+  }
+
+  QPen labelPen( mLabelColor );
+  p->setPen( labelPen );
+
+  //scale font (for printing)
+  QFont pixelSizeFont = mLabelFont;
+  pixelSizeFont.setPixelSize( context.outputLineWidth( mLabelFont.pointSizeF() * 0.3527 ) );
+  QFont scaledFont = pixelSizeFont;
+  scaledFont.setPixelSize( pixelSizeFont.pixelSize() * context.renderContext().rasterScaleFactor() );
+  p->setFont( scaledFont );
+
+  QFontMetricsF fontMetrics( pixelSizeFont );
+  QPointF currentLabelShift; //considers the signs to determine the label position
+
+  QList<QPointF>::const_iterator labelPosIt = labelShifts.constBegin();
+  QStringList::const_iterator text_it = labelList.constBegin();
+
+  for ( ; labelPosIt != labelShifts.constEnd() && text_it != labelList.constEnd(); ++labelPosIt, ++text_it )
+  {
+    currentLabelShift = *labelPosIt;
+    if ( currentLabelShift.x() < 0 )
+    {
+      currentLabelShift.setX( currentLabelShift.x() - fontMetrics.width( *text_it ) );
+    }
+    if ( currentLabelShift.y() > 0 )
+    {
+      currentLabelShift.setY( currentLabelShift.y() + fontMetrics.ascent() );
+    }
+
+    QPointF drawingPoint( centerPoint + currentLabelShift );
+    p->save();
+    p->translate( drawingPoint.x(), drawingPoint.y() );
+    p->scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
+    p->drawText( QPointF( 0, 0 ), *text_it );
+    p->restore();
+  }
+}

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.h
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.h	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrenderer.h	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,145 @@
+/***************************************************************************
+                              qgspointdisplacementrenderer.cpp
+                              --------------------------------
+  begin                : January 26, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco at hugis dot net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef QGSPOINTDISPLACEMENTRENDERER_H
+#define QGSPOINTDISPLACEMENTRENDERER_H
+
+#include "qgsfeature.h"
+#include "qgssymbolv2.h"
+#include "qgspoint.h"
+#include "qgsrendererv2.h"
+#include <QFont>
+#include <QSet>
+
+class QgsVectorLayer;
+
+/**A renderer that automatically displaces points with the same position*/
+class QgsPointDisplacementRenderer: public QgsFeatureRendererV2
+{
+  public:
+    QgsPointDisplacementRenderer( const QString& labelAttributeName = "" );
+    ~QgsPointDisplacementRenderer();
+
+    QgsFeatureRendererV2* clone();
+
+    /**Reimplemented from QgsFeatureRendererV2*/
+    void renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool drawVertexMarker = false );
+
+    QgsSymbolV2* symbolForFeature( QgsFeature& feature );
+
+    void startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer );
+
+    void stopRender( QgsRenderContext& context );
+
+    QList<QString> usedAttributes();
+    QgsSymbolV2List symbols();
+
+    //! create a renderer from XML element
+    static QgsFeatureRendererV2* create( QDomElement& symbologyElem );
+    QDomElement save( QDomDocument& doc );
+
+    QgsLegendSymbologyList legendSymbologyItems( QSize iconSize );
+
+    void setLabelAttributeName( const QString& name ) { mLabelAttributeName = name; }
+    QString labelAttributeName() const { return mLabelAttributeName; }
+
+    /**Sets embedded renderer (takes ownership)*/
+    void setEmbeddedRenderer( QgsFeatureRendererV2* r );
+    QgsFeatureRendererV2* embeddedRenderer() { return mRenderer;}
+
+    void setDisplacementGroups( const QList<QMap<int, QgsFeature> >& list );
+
+    void setLabelFont( const QFont& f ) { mLabelFont = f; }
+    QFont labelFont() const { return mLabelFont;}
+
+    void setCircleWidth( double w ) { mCircleWidth = w; }
+    double circleWidth() const { return mCircleWidth; }
+
+    void setCircleColor( const QColor& c ) { mCircleColor = c; }
+    QColor circleColor() const { return mCircleColor; }
+
+    void setLabelColor( const QColor& c ) { mLabelColor = c;}
+    QColor labelColor() const { return mLabelColor; }
+
+    void setCircleRadiusAddition( double d ) { mCircleRadiusAddition = d; }
+    double circleRadiusAddition() const { return mCircleRadiusAddition; }
+
+    void setMaxLabelScaleDenominator( double d ) { mMaxLabelScaleDenominator = d; }
+    double maxLabelScaleDenominator() const { return mMaxLabelScaleDenominator; }
+
+    /**Returns the symbol for the center of a displacement group (but _not_ ownership of the symbol)*/
+    QgsMarkerSymbolV2* centerSymbol() { return mCenterSymbol;}
+    /**Sets the center symbol (takes ownership)*/
+    void setCenterSymbol( QgsMarkerSymbolV2* symbol );
+
+  private:
+
+    /**Embedded renderer. Like This, it is possible to use a classification together with point displacement*/
+    QgsFeatureRendererV2* mRenderer;
+
+    /**Attribute name for labeling. Empty string means no labelling will be done*/
+    QString mLabelAttributeName;
+    /**Label attribute index (or -1 if none). This index is not stored, it is requested in the startRender() method*/
+    int mLabelIndex;
+
+    /**Center symbol for a displacement group*/
+    QgsMarkerSymbolV2* mCenterSymbol;
+
+    /**Tolerance. Points that are closer together are considered as equal*/
+    double mTolerance;
+
+    /**Font that is passed to the renderer*/
+    QFont mLabelFont;
+    QColor mLabelColor;
+    /**Line width for the circle*/
+    double mCircleWidth;
+    /**Color to draw the circle*/
+    QColor mCircleColor;
+    /**Addition to the default circle radius*/
+    double mCircleRadiusAddition;
+    /**Is set internally from startRender() depending on scale denominator*/
+    bool mDrawLabels;
+    /**Maximum scale denominator for label display. Negative number means no scale limitation*/
+    double mMaxLabelScaleDenominator;
+
+    /**Groups of features that have the same position*/
+    QList<QMap<int, QgsFeature> > mDisplacementGroups;
+    /**Set that contains all the ids the display groups (for quicker lookup)*/
+    QSet<int> mDisplacementIds;
+
+    /**Create the displacement groups efficiently using a spatial index*/
+    void createDisplacementGroups( QgsVectorLayer *vlayer, const QgsRectangle& viewExtent );
+    /**Creates a search rectangle with mTolerance*/
+    QgsRectangle searchRect( const QgsPoint& p ) const;
+    /**This is a debugging function to check the entries in the displacement groups*/
+    void printInfoDisplacementGroups();
+
+    /**Returns the label for a feature (using mLabelAttributeName as attribute field)*/
+    QString getLabel( const QgsFeature& f );
+
+    //rendering methods
+    void renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context, const QList<QgsMarkerSymbolV2*>& symbols, \
+                      const QStringList& labels );
+
+    //helper functions
+    void calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius, double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const;
+    void drawCircle( double radiusPainterUnits, QgsSymbolV2RenderContext& context, const QPointF& centerPoint, int nSymbols );
+    void drawSymbols( QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions );
+    void drawLabels( const QPointF& centerPoint, QgsSymbolV2RenderContext& context, const QList<QPointF>& labelShifts, const QStringList& labelList );
+};
+
+#endif // QGSPOINTDISPLACEMENTRENDERER_H

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.cpp
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.cpp	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.cpp	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,335 @@
+/***************************************************************************
+                              qgspointdisplacementrendererwidget.cpp
+                              --------------------------------------
+  begin                : January 26, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco at hugis dot net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "qgspointdisplacementrendererwidget.h"
+#include "qgspointdisplacementrenderer.h"
+#include "qgsrendererv2registry.h"
+#include "qgsfield.h"
+#include "qgsstylev2.h"
+#include "qgssymbolv2selectordialog.h"
+#include "qgssymbollayerv2utils.h"
+#include "qgsvectorlayer.h"
+#include <QColorDialog>
+#include <QFontDialog>
+
+QgsRendererV2Widget* QgsPointDisplacementRendererWidget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
+{
+  return new QgsPointDisplacementRendererWidget( layer, style, renderer );
+}
+
+QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ): \
+    QgsRendererV2Widget( layer, style ), mEmbeddedRendererWidget( 0 )
+{
+  if ( !layer )
+  {
+    return;
+  }
+
+  //the renderer only applies to point vector layers
+  if ( layer->wkbType() != QGis::WKBPoint && layer->wkbType()  != QGis::WKBPoint25D )
+  {
+    //setup blank dialog
+    mRenderer = 0;
+    setupBlankUi( layer->name() );
+    return;
+  }
+  setupUi( this );
+
+  if ( renderer && renderer->type() == "pointDisplacement" )
+  {
+    mRenderer = dynamic_cast<QgsPointDisplacementRenderer*>( renderer->clone() );
+  }
+  else
+  {
+    mRenderer = new QgsPointDisplacementRenderer();
+  }
+
+  blockAllSignals( true );
+
+  //insert attributes into combo box
+  if ( layer )
+  {
+    const QgsFieldMap layerAttributes = layer->pendingFields();
+    QgsFieldMap::const_iterator it = layerAttributes.constBegin();
+    for ( ; it != layerAttributes.constEnd(); ++it )
+    {
+      mLabelFieldComboBox->addItem( it.value().name() );
+    }
+    mLabelFieldComboBox->addItem( tr( "None" ) );
+
+    QString currentLabelAttribute = mRenderer->labelAttributeName();
+    if ( !currentLabelAttribute.isEmpty() )
+    {
+      mLabelFieldComboBox->setCurrentIndex( mLabelFieldComboBox->findText( currentLabelAttribute ) );
+    }
+    else
+    {
+      mLabelFieldComboBox->setCurrentIndex( mLabelFieldComboBox->findText( tr( "None" ) ) );
+    }
+  }
+
+  //insert possible renderer types
+  QStringList rendererList = QgsRendererV2Registry::instance()->renderersList();
+  QStringList::const_iterator it = rendererList.constBegin();
+  for ( ; it != rendererList.constEnd(); ++it )
+  {
+    if ( *it != "pointDisplacement" )
+    {
+      QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( *it );
+      mRendererComboBox->addItem( m->icon(), m->visibleName(), *it );
+    }
+  }
+
+  mCircleWidthSpinBox->setValue( mRenderer->circleWidth() );
+  mCircleColorButton->setColor( mRenderer->circleColor() );
+  mLabelColorButton->setColor( mRenderer->labelColor() );
+  mCircleModificationSpinBox->setValue( mRenderer->circleRadiusAddition() );
+
+  //scale dependent labelling
+  mMaxScaleDenominatorEdit->setText( QString::number( mRenderer->maxLabelScaleDenominator() ) );
+  mMaxScaleDenominatorEdit->setValidator( new QDoubleValidator( mMaxScaleDenominatorEdit ) );
+  if ( mRenderer->maxLabelScaleDenominator() > 0 )
+  {
+    mScaleDependentLabelsCheckBox->setCheckState( Qt::Checked );
+  }
+  else
+  {
+    mScaleDependentLabelsCheckBox->setCheckState( Qt::Unchecked );
+    mMaxScaleDenominatorEdit->setEnabled( false );
+  }
+
+
+  blockAllSignals( false );
+
+  //set the appropriate renderer dialog
+  if ( mRenderer && mRenderer->embeddedRenderer() )
+  {
+    QString rendererName = mRenderer->embeddedRenderer()->type();
+    int rendererIndex = mRendererComboBox->findData( rendererName );
+    if ( rendererIndex != -1 )
+    {
+      mRendererComboBox->setCurrentIndex( rendererIndex );
+      on_mRendererComboBox_currentIndexChanged( rendererIndex );
+    }
+  }
+
+  updateCenterIcon();
+}
+
+QgsPointDisplacementRendererWidget::~QgsPointDisplacementRendererWidget()
+{
+  delete mRenderer;
+  delete mEmbeddedRendererWidget;
+}
+
+QgsFeatureRendererV2* QgsPointDisplacementRendererWidget::renderer()
+{
+  if ( mRenderer && mEmbeddedRendererWidget )
+  {
+    QgsFeatureRendererV2* embeddedRenderer = mEmbeddedRendererWidget->renderer();
+    if ( embeddedRenderer )
+    {
+      mRenderer->setEmbeddedRenderer( embeddedRenderer->clone() );
+    }
+  }
+  return mRenderer;
+}
+
+void QgsPointDisplacementRendererWidget::on_mLabelFieldComboBox_currentIndexChanged( const QString& text )
+{
+  if ( mRenderer )
+  {
+    if ( text == tr( "None" ) )
+    {
+      mRenderer->setLabelAttributeName( "" );
+    }
+    else
+    {
+      mRenderer->setLabelAttributeName( text );
+    }
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mRendererComboBox_currentIndexChanged( int index )
+{
+  QString rendererId = mRendererComboBox->itemData( index ).toString();
+  QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( rendererId );
+  if ( m )
+  {
+    delete mEmbeddedRendererWidget;
+    mEmbeddedRendererWidget = m->createRendererWidget( mLayer, mStyle, mRenderer->embeddedRenderer()->clone() );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mRendererSettingsButton_clicked()
+{
+  if ( mEmbeddedRendererWidget )
+  {
+    //create a dialog with the embedded widget
+    QDialog* d = new QDialog();
+    QGridLayout* layout = new QGridLayout( d );
+    mEmbeddedRendererWidget->setParent( d );
+    QDialogButtonBox* buttonBox = new QDialogButtonBox( d );
+    buttonBox->addButton( QDialogButtonBox::Ok );
+    QObject::connect( buttonBox, SIGNAL( accepted() ), d, SLOT( accept() ) );
+    layout->addWidget( mEmbeddedRendererWidget, 0, 0 );
+    layout->addWidget( buttonBox, 1, 0 );
+    d->exec();
+    mEmbeddedRendererWidget->setParent( 0 );
+    delete d;
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mLabelFontButton_clicked()
+{
+  if ( !mRenderer )
+  {
+    return;
+  }
+
+  bool ok;
+  QFont newFont = QFontDialog::getFont( &ok, mRenderer->labelFont(), 0, tr( "Label Font" ) );
+  if ( ok )
+  {
+    mRenderer->setLabelFont( newFont );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mCircleWidthSpinBox_valueChanged( double d )
+{
+  if ( mRenderer )
+  {
+    mRenderer->setCircleWidth( d );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mCircleColorButton_clicked()
+{
+  if ( !mRenderer )
+  {
+    return;
+  }
+
+  QColor newColor = QColorDialog::getColor( mRenderer->circleColor(), 0, tr( "Circle color" ), QColorDialog::ShowAlphaChannel );
+  if ( newColor.isValid() )
+  {
+    mRenderer->setCircleColor( newColor );
+    mCircleColorButton->setColor( newColor );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mLabelColorButton_clicked()
+{
+  if ( !mRenderer )
+  {
+    return;
+  }
+
+  QColor newColor = QColorDialog::getColor( mRenderer->labelColor(), 0, tr( "Label color" ), QColorDialog::ShowAlphaChannel );
+  if ( newColor.isValid() )
+  {
+    mRenderer->setLabelColor( newColor );
+    mLabelColorButton->setColor( newColor );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mCircleModificationSpinBox_valueChanged( double d )
+{
+  if ( !mRenderer )
+  {
+    return;
+  }
+
+  mRenderer->setCircleRadiusAddition( d );
+}
+
+void QgsPointDisplacementRendererWidget::on_mScaleDependentLabelsCheckBox_stateChanged( int state )
+{
+  if ( state == Qt::Unchecked )
+  {
+    mMaxScaleDenominatorEdit->setText( "-1" );
+    mMaxScaleDenominatorEdit->setEnabled( false );
+  }
+  else
+  {
+    mMaxScaleDenominatorEdit->setEnabled( true );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::on_mMaxScaleDenominatorEdit_textChanged( const QString & text )
+{
+  if ( !mRenderer )
+  {
+    return;
+  }
+
+  bool ok;
+  double scaleDenominator = text.toDouble( &ok );
+  if ( ok )
+  {
+    mRenderer->setMaxLabelScaleDenominator( scaleDenominator );
+  }
+}
+
+void QgsPointDisplacementRendererWidget::blockAllSignals( bool block )
+{
+  mLabelFieldComboBox->blockSignals( block );
+  mLabelFontButton->blockSignals( block );
+  mCircleWidthSpinBox->blockSignals( block );
+  mCircleColorButton->blockSignals( block );
+  mRendererComboBox->blockSignals( block );
+  mLabelColorButton->blockSignals( block );
+  mCircleModificationSpinBox->blockSignals( block );
+  mScaleDependentLabelsCheckBox->blockSignals( block );
+  mMaxScaleDenominatorEdit->blockSignals( block );
+  mCenterSymbolPushButton->blockSignals( block );
+}
+
+void QgsPointDisplacementRendererWidget::on_mCenterSymbolPushButton_clicked()
+{
+  if ( !mRenderer || !mRenderer->centerSymbol() )
+  {
+    return;
+  }
+  QgsMarkerSymbolV2* markerSymbol = dynamic_cast<QgsMarkerSymbolV2*>( mRenderer->centerSymbol()->clone() );
+  QgsSymbolV2SelectorDialog dlg( markerSymbol, QgsStyleV2::defaultStyle(), this );
+  if ( dlg.exec() == QDialog::Rejected )
+  {
+    delete markerSymbol;
+    return;
+  }
+  mRenderer->setCenterSymbol( markerSymbol );
+  updateCenterIcon();
+}
+
+void QgsPointDisplacementRendererWidget::updateCenterIcon()
+{
+  QgsMarkerSymbolV2* symbol = mRenderer->centerSymbol();
+  if ( !symbol )
+  {
+    return;
+  }
+  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( symbol, mCenterSymbolPushButton->iconSize() );
+  mCenterSymbolPushButton->setIcon( icon );
+}
+
+void QgsPointDisplacementRendererWidget::setupBlankUi( const QString& layerName )
+{
+  QGridLayout* layout = new QGridLayout( this );
+  QLabel* label = new QLabel( tr( "The point displacement renderer only applies to (single) point layers. \n'%1' is not a point layer and cannot be displayed by the point displacement renderer" ).arg( layerName ), this );
+  layout->addWidget( label );
+}

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.h
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.h	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidget.h	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,58 @@
+/***************************************************************************
+                              qgspointdisplacementrendererwidget.h
+                              ------------------------------------
+  begin                : January 26, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco at hugis dot net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef QGSPOINTDISPLACEMENTRENDERERWIDGET_H
+#define QGSPOINTDISPLACEMENTRENDERERWIDGET_H
+
+#include "ui_qgspointdisplacementrendererwidgetbase.h"
+#include "qgsrendererv2widget.h"
+
+class QgsPointDisplacementRenderer;
+
+class QgsPointDisplacementRendererWidget: public QgsRendererV2Widget, private Ui::QgsPointDisplacementRendererWidgetBase
+{
+    Q_OBJECT
+  public:
+    static QgsRendererV2Widget* create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
+    QgsPointDisplacementRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
+    ~QgsPointDisplacementRendererWidget();
+
+    QgsFeatureRendererV2* renderer();
+
+  private:
+    QgsPointDisplacementRenderer* mRenderer;
+    QgsRendererV2Widget* mEmbeddedRendererWidget;
+
+    void blockAllSignals( bool block );
+    void updateCenterIcon();
+    void setupBlankUi( const QString& layerName );
+
+  private slots:
+    void on_mLabelFieldComboBox_currentIndexChanged( const QString& text );
+    void on_mRendererComboBox_currentIndexChanged( int index );
+    void on_mLabelFontButton_clicked();
+    void on_mCircleWidthSpinBox_valueChanged( double d );
+    void on_mCircleColorButton_clicked();
+    void on_mLabelColorButton_clicked();
+    void on_mCircleModificationSpinBox_valueChanged( double d );
+    void on_mScaleDependentLabelsCheckBox_stateChanged( int state );
+    void on_mMaxScaleDenominatorEdit_textChanged( const QString & text );
+    void on_mCenterSymbolPushButton_clicked();
+    void on_mRendererSettingsButton_clicked();
+};
+
+#endif // QGSPOINTDISPLACEMENTRENDERERWIDGET_H

Added: trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidgetbase.ui
===================================================================
--- trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidgetbase.ui	                        (rev 0)
+++ trunk/qgis/src/plugins/point_displacement_renderer/qgspointdisplacementrendererwidgetbase.ui	2010-03-21 22:14:14 UTC (rev 13139)
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QgsPointDisplacementRendererWidgetBase</class>
+ <widget class="QWidget" name="QgsPointDisplacementRendererWidgetBase">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>328</width>
+    <height>469</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="0" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_9">
+     <item>
+      <widget class="QLabel" name="mCenterSymbolLabel">
+       <property name="text">
+        <string>Center symbol:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="mCenterSymbolPushButton">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="mRendererLabel">
+       <property name="text">
+        <string>Renderer:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="mRendererComboBox"/>
+     </item>
+    </layout>
+   </item>
+   <item row="2" column="0">
+    <widget class="QPushButton" name="mRendererSettingsButton">
+     <property name="text">
+      <string>Renderer settings...</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QGroupBox" name="mDisplacementCirclesGroupBox">
+     <property name="title">
+      <string>Displacement circles</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QLabel" name="mCircleWidthLabel">
+          <property name="text">
+           <string>Circle pen width:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="mCircleWidthSpinBox"/>
+        </item>
+       </layout>
+      </item>
+      <item row="1" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QLabel" name="mCircleColorLabel">
+          <property name="text">
+           <string>Circle color:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QgsColorButton" name="mCircleColorButton">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="2" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_6">
+        <item>
+         <widget class="QLabel" name="mCircleRadiusLabel">
+          <property name="text">
+           <string>Circle radius modification:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="mCircleModificationSpinBox">
+          <property name="minimum">
+           <double>-99.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QGroupBox" name="mLabellingGroupBox">
+     <property name="title">
+      <string>Labels</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="mLabelAttributeLabel">
+          <property name="text">
+           <string>Label attribute:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="mLabelFieldComboBox"/>
+        </item>
+       </layout>
+      </item>
+      <item row="1" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_8">
+        <item>
+         <widget class="QPushButton" name="mLabelFontButton">
+          <property name="text">
+           <string>Label font...</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>94</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item row="2" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_5">
+        <item>
+         <widget class="QLabel" name="mLabelColorLabel">
+          <property name="text">
+           <string>Label color:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QgsColorButton" name="mLabelColorButton">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="3" column="0">
+       <widget class="QCheckBox" name="mScaleDependentLabelsCheckBox">
+        <property name="text">
+         <string>Use scale dependent labelling</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_7">
+        <item>
+         <widget class="QLabel" name="mMaxScaleLabel">
+          <property name="text">
+           <string>max scale denominator:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="mMaxScaleDenominatorEdit"/>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QgsColorButton</class>
+   <extends>QToolButton</extends>
+   <header>qgscolorbutton.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>



More information about the QGIS-commit mailing list