QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgssizescalewidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssizescalewidget.cpp - continuous size scale assistant
3 
4  ---------------------
5  begin : March 2015
6  copyright : (C) 2015 by Vincent Mora
7  email : vincent dot mora at oslandia dot com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgssizescalewidget.h"
18 
19 #include "qgsvectorlayer.h"
20 #include "qgsmaplayerregistry.h"
21 #include "qgssymbolv2.h"
22 #include "qgslayertreelayer.h"
24 #include "qgssymbollayerv2utils.h"
25 #include "qgsscaleexpression.h"
26 #include "qgsdatadefined.h"
27 
28 #include <QMenu>
29 #include <QAction>
30 #include <QItemDelegate>
31 
32 #include <limits>
33 
34 
36 {
37  public:
38  ItemDelegate( QStandardItemModel* model ): mModel( model ) {}
39 
40  QSize sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex & index ) const override
41  {
42  return mModel->item( index.row() )->icon().actualSize( QSize( 512, 512 ) );
43  }
44 
45  private:
46  QStandardItemModel* mModel;
47 
48 };
49 
50 void QgsSizeScaleWidget::setFromSymbol()
51 {
52  if ( !mSymbol )
53  {
54  return;
55  }
56 
57  QgsDataDefined ddSize = mSymbol->dataDefinedSize();
58  QgsScaleExpression expr( ddSize.expressionString() );
59  if ( expr )
60  {
61  for ( int i = 0; i < scaleMethodComboBox->count(); i++ )
62  {
63  if ( scaleMethodComboBox->itemData( i ).toInt() == int( expr.type() ) )
64  {
65  scaleMethodComboBox->setCurrentIndex( i );
66  break;
67  }
68  }
69 
70  mExpressionWidget->setField( expr.baseExpression() );
71 
72  minValueSpinBox->setValue( expr.minValue() );
73  maxValueSpinBox->setValue( expr.maxValue() );
74  minSizeSpinBox->setValue( expr.minSize() );
75  maxSizeSpinBox->setValue( expr.maxSize() );
76  nullSizeSpinBox->setValue( expr.nullSize() );
77  }
78  updatePreview();
79 }
80 
82  : mSymbol( symbol )
83  // we just use the minimumValue and maximumValue from the layer, unfortunately they are
84  // non const, so we get the layer from the registry instead
85  , mLayer( layer ? dynamic_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( layer->id() ) ) : 0 )
86 {
87  setupUi( this );
88  setWindowFlags( Qt::WindowStaysOnTopHint );
89 
90  if ( mLayer )
91  {
92  mLayerTreeLayer = new QgsLayerTreeLayer( mLayer );
93  mRoot.addChildNode( mLayerTreeLayer ); // takes ownership
94  }
95  else
96  {
97  mLayerTreeLayer = 0;
98  }
99 
100  treeView->setModel( &mPreviewList );
101  treeView->setItemDelegate( new ItemDelegate( &mPreviewList ) );
102  treeView->setHeaderHidden( true );
103  treeView->expandAll();
104 
105  QAction* computeFromLayer = new QAction( tr( "Compute from layer" ), this );
106  connect( computeFromLayer, SIGNAL( triggered() ), this, SLOT( computeFromLayerTriggered() ) );
107 
108  QMenu* menu = new QMenu();
109  menu->addAction( computeFromLayer );
110  computeValuesButton->setMenu( menu );
111  connect( computeValuesButton, SIGNAL( clicked() ), computeValuesButton, SLOT( showMenu() ) );
112 
113  //mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
114  if ( mLayer )
115  {
116  mExpressionWidget->setLayer( mLayer );
117  }
118 
119  scaleMethodComboBox->addItem( tr( "Flannery" ), int( QgsScaleExpression::Flannery ) );
120  scaleMethodComboBox->addItem( tr( "Surface" ), int( QgsScaleExpression::Area ) );
121  scaleMethodComboBox->addItem( tr( "Radius" ), int( QgsScaleExpression::Linear ) );
122 
123  minSizeSpinBox->setShowClearButton( false );
124  maxSizeSpinBox->setShowClearButton( false );
125  minValueSpinBox->setShowClearButton( false );
126  maxValueSpinBox->setShowClearButton( false );
127  nullSizeSpinBox->setShowClearButton( false );
128 
129  // setup ui from expression if any
130  setFromSymbol();
131 
132  connect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
133  connect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
134  connect( minValueSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
135  connect( maxValueSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
136  connect( nullSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
137  //potentially very expensive for large layers:
138  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( computeFromLayerTriggered() ) );
139  connect( scaleMethodComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( updatePreview() ) );
140 }
141 
143 {
144  QScopedPointer<QgsScaleExpression> exp( createExpression() );
145  return QgsDataDefined( exp.data() );
146 }
147 
149 {
150  setFromSymbol();
151 }
152 
153 QgsScaleExpression *QgsSizeScaleWidget::createExpression() const
154 {
155  return new QgsScaleExpression( QgsScaleExpression::Type( scaleMethodComboBox->itemData( scaleMethodComboBox->currentIndex() ).toInt() ),
156  mExpressionWidget->currentField(),
157  minValueSpinBox->value(),
158  maxValueSpinBox->value(),
159  minSizeSpinBox->value(),
160  maxSizeSpinBox->value(),
161  nullSizeSpinBox->value() );
162 }
163 
164 void QgsSizeScaleWidget::updatePreview()
165 {
166  if ( !mSymbol || !mLayer )
167  return;
168 
169  QScopedPointer<QgsScaleExpression> expr( createExpression() );
170  QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks( expr->minValue(), expr->maxValue(), 4 );
171 
172  treeView->setIconSize( QSize( 512, 512 ) );
173  mPreviewList.clear();
174  int widthMax = 0;
175  for ( int i = 0; i < breaks.length(); i++ )
176  {
177  QScopedPointer< QgsMarkerSymbolV2 > symbol( dynamic_cast<QgsMarkerSymbolV2*>( mSymbol->clone() ) );
178  symbol->setDataDefinedSize( QgsDataDefined() );
179  symbol->setDataDefinedAngle( QgsDataDefined() ); // to avoid symbol not beeing drawn
180  symbol->setSize( expr->size( breaks[i] ) );
181  QgsSymbolV2LegendNode node( mLayerTreeLayer, QgsLegendSymbolItemV2( symbol.data(), QString::number( i ), 0 ) );
182  const QSize sz( node.minimumIconSize() );
183  node.setIconSize( sz );
184  QScopedPointer< QStandardItem > item( new QStandardItem( node.data( Qt::DecorationRole ).value<QPixmap>(), QString::number( breaks[i] ) ) );
185  widthMax = qMax( sz.width(), widthMax );
186  mPreviewList.appendRow( item.take() );
187  }
188 
189  // center icon and align text left by giving icons the same width
190  // @todo maybe add some space so that icons don't touch
191  for ( int i = 0; i < breaks.length(); i++ )
192  {
193  QPixmap img( mPreviewList.item( i )->icon().pixmap( mPreviewList.item( i )->icon().actualSize( QSize( 512, 512 ) ) ) );
194  QPixmap enlarged( widthMax, img.height() );
195  // fill transparent and add original image
196  enlarged.fill( Qt::transparent );
197  QPainter p( &enlarged );
198  p.drawPixmap( QPoint(( widthMax - img.width() ) / 2, 0 ), img );
199  p.end();
200  mPreviewList.item( i )->setIcon( enlarged );
201  }
202 }
203 
204 void QgsSizeScaleWidget::computeFromLayerTriggered()
205 {
206  if ( !mLayer )
207  return;
208 
209  QgsExpression expression( mExpressionWidget->currentField() );
210  if ( ! expression.prepare( mLayer->pendingFields() ) )
211  return;
212 
213  QStringList lst( expression.referencedColumns() );
214 
215  QgsFeatureIterator fit = mLayer->getFeatures(
216  QgsFeatureRequest().setFlags( expression.needsGeometry()
219  .setSubsetOfAttributes( lst, mLayer->pendingFields() ) );
220 
221  // create list of non-null attribute values
222  double min = DBL_MAX;
223  double max = -DBL_MAX;
224  QgsFeature f;
225  while ( fit.nextFeature( f ) )
226  {
227  bool ok;
228  const double value = expression.evaluate( f ).toDouble( &ok );
229  if ( ok )
230  {
231  max = qMax( max, value );
232  min = qMin( min, value );
233  }
234  }
235  minValueSpinBox->setValue( min );
236  maxValueSpinBox->setValue( max );
237  updatePreview();
238 }
239 
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
Wrapper for iterator of features from vector data provider or vector layer.
static unsigned index
void setupUi(QWidget *widget)
void setIcon(const QIcon &icon)
A container class for data source field mapping or expression.
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about 'classes' equally spaced round values which cover the range of values fr...
void fill(const QColor &color)
int length() const
QgsDataDefined dataDefined() const override
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
void addAction(QAction *action)
QString expressionString() const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:162
QString tr(const char *sourceText, const char *disambiguation, int n)
QPixmap pixmap(const QSize &size, Mode mode, State state) const
ItemDelegate(QStandardItemModel *model)
virtual void showEvent(QShowEvent *) override
QString number(int n, int base)
int row() const
This class wraps a request for features to a vector layer (or directly its vector data provider)...
virtual QgsSymbolV2 * clone() const override
QSize actualSize(const QSize &size, Mode mode, State state) const
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &index) const override
This class tracks map layers that are currently loaded and provides a means to fetch a pointer to a m...
T * data() const
QStandardItem * item(int row, int column) const
int min(int a, int b)
Definition: util.h:93
void setWindowFlags(QFlags< Qt::WindowType > type)
void addChildNode(QgsLayerTreeNode *node)
Append an existing node. The node must not have a parent yet. The node will be owned by this group...
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
Geometry is not required. It may still be returned if e.g. required for a filter condition.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Represents a vector layer which manages a vector based data sets.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
int max(int a, int b)
Definition: util.h:87
void appendRow(const QList< QStandardItem * > &items)
QgsSizeScaleWidget(const QgsVectorLayer *layer, const QgsMarkerSymbolV2 *symbol)
QIcon icon() const
Layer tree node points to a map layer.