QGIS API Documentation  2.9.0-Master
qgsdiagramrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdiagramrendererv2.cpp
3  ---------------------
4  begin : March 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsdiagramrendererv2.h"
16 #include "qgsvectorlayer.h"
17 #include "diagram/qgstextdiagram.h"
18 #include "diagram/qgspiediagram.h"
20 #include "qgsrendercontext.h"
22 
23 #include <QDomElement>
24 #include <QPainter>
25 
27  : placement( AroundPoint )
28  , placementFlags( OnLine )
29  , priority( 5 )
30  , obstacle( false )
31  , dist( 0.0 )
32  , renderer( 0 )
33  , palLayer( 0 )
34  , ct( 0 )
35  , xform( 0 )
36  , xPosColumn( -1 )
37  , yPosColumn( -1 )
38  , showAll( true )
39 {
40 }
41 
43 {
44  delete renderer;
45 }
46 
47 void QgsDiagramLayerSettings::readXML( const QDomElement& elem, const QgsVectorLayer* layer )
48 {
49  Q_UNUSED( layer )
50 
51  placement = ( Placement )elem.attribute( "placement" ).toInt();
52  placementFlags = ( LinePlacementFlags )elem.attribute( "linePlacementFlags" ).toInt();
53  priority = elem.attribute( "priority" ).toInt();
54  obstacle = elem.attribute( "obstacle" ).toInt();
55  dist = elem.attribute( "dist" ).toDouble();
56  xPosColumn = elem.attribute( "xPosColumn" ).toInt();
57  yPosColumn = elem.attribute( "yPosColumn" ).toInt();
58  showAll = ( elem.attribute( "showAll", "0" ) != "0" );
59 }
60 
61 void QgsDiagramLayerSettings::writeXML( QDomElement& layerElem, QDomDocument& doc, const QgsVectorLayer* layer ) const
62 {
63  Q_UNUSED( layer )
64 
65  QDomElement diagramLayerElem = doc.createElement( "DiagramLayerSettings" );
66  diagramLayerElem.setAttribute( "placement", placement );
67  diagramLayerElem.setAttribute( "linePlacementFlags", placementFlags );
68  diagramLayerElem.setAttribute( "priority", priority );
69  diagramLayerElem.setAttribute( "obstacle", obstacle );
70  diagramLayerElem.setAttribute( "dist", QString::number( dist ) );
71  diagramLayerElem.setAttribute( "xPosColumn", xPosColumn );
72  diagramLayerElem.setAttribute( "yPosColumn", yPosColumn );
73  diagramLayerElem.setAttribute( "showAll", showAll );
74  layerElem.appendChild( diagramLayerElem );
75 }
76 
77 void QgsDiagramSettings::readXML( const QDomElement& elem, const QgsVectorLayer* layer )
78 {
79  Q_UNUSED( layer );
80 
81  enabled = ( elem.attribute( "enabled", "1" ) != "0" );
82  font.fromString( elem.attribute( "font" ) );
83  backgroundColor.setNamedColor( elem.attribute( "backgroundColor" ) );
84  backgroundColor.setAlpha( elem.attribute( "backgroundAlpha" ).toInt() );
85  size.setWidth( elem.attribute( "width" ).toDouble() );
86  size.setHeight( elem.attribute( "height" ).toDouble() );
87  transparency = elem.attribute( "transparency", "0" ).toInt();
88  penColor.setNamedColor( elem.attribute( "penColor" ) );
89  int penAlpha = elem.attribute( "penAlpha", "255" ).toInt();
90  penColor.setAlpha( penAlpha );
91  penWidth = elem.attribute( "penWidth" ).toDouble();
92 
93  minScaleDenominator = elem.attribute( "minScaleDenominator", "-1" ).toDouble();
94  maxScaleDenominator = elem.attribute( "maxScaleDenominator", "-1" ).toDouble();
95  if ( elem.hasAttribute( "scaleBasedVisibility" ) )
96  {
97  scaleBasedVisibility = ( elem.attribute( "scaleBasedVisibility", "1" ) != "0" );
98  }
99  else
100  {
102  }
103 
104  //mm vs map units
105  if ( elem.attribute( "sizeType" ) == "MM" )
106  {
107  sizeType = MM;
108  }
109  else
110  {
111  sizeType = MapUnits;
112  }
113 
114  //label placement method
115  if ( elem.attribute( "labelPlacementMethod" ) == "Height" )
116  {
118  }
119  else
120  {
122  }
123 
124  // orientation
125  if ( elem.attribute( "diagramOrientation" ) == "Left" )
126  {
128  }
129  else if ( elem.attribute( "diagramOrientation" ) == "Right" )
130  {
132  }
133  else if ( elem.attribute( "diagramOrientation" ) == "Down" )
134  {
136  }
137  else
138  {
140  }
141 
142  // scale dependency
143  if ( elem.attribute( "scaleDependency" ) == "Diameter" )
144  {
145  scaleByArea = false;
146  }
147  else
148  {
149  scaleByArea = true;
150  }
151 
152  barWidth = elem.attribute( "barWidth" ).toDouble();
153 
154  angleOffset = elem.attribute( "angleOffset" ).toInt();
155 
156  minimumSize = elem.attribute( "minimumSize" ).toDouble();
157 
158  //colors
159  categoryColors.clear();
160  QDomNodeList attributes = elem.elementsByTagName( "attribute" );
161 
162  if ( attributes.length() > 0 )
163  {
164  for ( uint i = 0; i < attributes.length(); i++ )
165  {
166  QDomElement attrElem = attributes.at( i ).toElement();
167  QColor newColor( attrElem.attribute( "color" ) );
168  newColor.setAlpha( 255 - transparency );
169  categoryColors.append( newColor );
170  categoryAttributes.append( attrElem.attribute( "field" ) );
171  categoryLabels.append( attrElem.attribute( "label" ) );
172  if ( categoryLabels.back().isEmpty() )
173  {
174  categoryLabels.back() = categoryAttributes.back();
175  }
176  }
177  }
178  else
179  {
180  // Restore old format attributes and colors
181 
182  QStringList colorList = elem.attribute( "colors" ).split( "/" );
183  QStringList::const_iterator colorIt = colorList.constBegin();
184  for ( ; colorIt != colorList.constEnd(); ++colorIt )
185  {
186  QColor newColor( *colorIt );
187  newColor.setAlpha( 255 - transparency );
188  categoryColors.append( QColor( newColor ) );
189  }
190 
191  //attribute indices
192  categoryAttributes.clear();
193  QStringList catList = elem.attribute( "categories" ).split( "/" );
194  QStringList::const_iterator catIt = catList.constBegin();
195  for ( ; catIt != catList.constEnd(); ++catIt )
196  {
197  categoryAttributes.append( *catIt );
198  categoryLabels.append( *catIt );
199  }
200  }
201 }
202 
203 void QgsDiagramSettings::writeXML( QDomElement& rendererElem, QDomDocument& doc, const QgsVectorLayer* layer ) const
204 {
205  Q_UNUSED( layer );
206 
207  QDomElement categoryElem = doc.createElement( "DiagramCategory" );
208  categoryElem.setAttribute( "enabled", enabled );
209  categoryElem.setAttribute( "font", font.toString() );
210  categoryElem.setAttribute( "backgroundColor", backgroundColor.name() );
211  categoryElem.setAttribute( "backgroundAlpha", backgroundColor.alpha() );
212  categoryElem.setAttribute( "width", QString::number( size.width() ) );
213  categoryElem.setAttribute( "height", QString::number( size.height() ) );
214  categoryElem.setAttribute( "penColor", penColor.name() );
215  categoryElem.setAttribute( "penAlpha", penColor.alpha() );
216  categoryElem.setAttribute( "penWidth", QString::number( penWidth ) );
217  categoryElem.setAttribute( "scaleBasedVisibility", scaleBasedVisibility );
218  categoryElem.setAttribute( "minScaleDenominator", QString::number( minScaleDenominator ) );
219  categoryElem.setAttribute( "maxScaleDenominator", QString::number( maxScaleDenominator ) );
220  categoryElem.setAttribute( "transparency", QString::number( transparency ) );
221 
222  // site type (mm vs. map units)
223  if ( sizeType == MM )
224  {
225  categoryElem.setAttribute( "sizeType", "MM" );
226  }
227  else
228  {
229  categoryElem.setAttribute( "sizeType", "MapUnits" );
230  }
231 
232  // label placement method (text diagram)
233  if ( labelPlacementMethod == Height )
234  {
235  categoryElem.setAttribute( "labelPlacementMethod", "Height" );
236  }
237  else
238  {
239  categoryElem.setAttribute( "labelPlacementMethod", "XHeight" );
240  }
241 
242  if ( scaleByArea )
243  {
244  categoryElem.setAttribute( "scaleDependency", "Area" );
245  }
246  else
247  {
248  categoryElem.setAttribute( "scaleDependency", "Diameter" );
249  }
250 
251  // orientation (histogram)
252  switch ( diagramOrientation )
253  {
254  case Left:
255  categoryElem.setAttribute( "diagramOrientation", "Left" );
256  break;
257 
258  case Right:
259  categoryElem.setAttribute( "diagramOrientation", "Right" );
260  break;
261 
262  case Down:
263  categoryElem.setAttribute( "diagramOrientation", "Down" );
264  break;
265 
266  case Up:
267  categoryElem.setAttribute( "diagramOrientation", "Up" );
268  break;
269 
270  default:
271  categoryElem.setAttribute( "diagramOrientation", "Up" );
272  break;
273  }
274 
275  categoryElem.setAttribute( "barWidth", QString::number( barWidth ) );
276  categoryElem.setAttribute( "minimumSize", QString::number( minimumSize ) );
277  categoryElem.setAttribute( "angleOffset", QString::number( angleOffset ) );
278 
279  QString colors;
280  int nCats = qMin( categoryColors.size(), categoryAttributes.size() );
281  for ( int i = 0; i < nCats; ++i )
282  {
283  QDomElement attributeElem = doc.createElement( "attribute" );
284 
285  attributeElem.setAttribute( "field", categoryAttributes.at( i ) );
286  attributeElem.setAttribute( "color", categoryColors.at( i ).name() );
287  attributeElem.setAttribute( "label", categoryLabels.at( i ) );
288  categoryElem.appendChild( attributeElem );
289  }
290 
291  rendererElem.appendChild( categoryElem );
292 }
293 
295  : mDiagram( 0 )
296 {
297 }
298 
300 {
301  delete mDiagram;
302 }
303 
305 {
306  delete mDiagram;
307  mDiagram = d;
308 }
309 
311  : mDiagram( other.mDiagram ? other.mDiagram->clone() : 0 )
312 {
313 }
314 
315 void QgsDiagramRendererV2::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QPointF& pos )
316 {
317  if ( !mDiagram )
318  {
319  return;
320  }
321 
323  if ( !diagramSettings( feature, c, s ) )
324  {
325  return;
326  }
327 
328  mDiagram->renderDiagram( feature, c, s, pos );
329 }
330 
332 {
334  if ( !diagramSettings( feature, c, s ) )
335  {
336  return QSizeF();
337  }
338 
339  QSizeF size = diagramSize( feature, c );
340  if ( s.sizeType == QgsDiagramSettings::MM )
341  {
342  convertSizeToMapUnits( size, c );
343  }
344  return size;
345 }
346 
348 {
349  if ( !size.isValid() )
350  {
351  return;
352  }
353 
354  double pixelToMap = context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
355  size.rwidth() *= pixelToMap;
356  size.rheight() *= pixelToMap;
357 }
358 
359 int QgsDiagramRendererV2::dpiPaintDevice( const QPainter* painter )
360 {
361  if ( painter )
362  {
363  QPaintDevice* device = painter->device();
364  if ( device )
365  {
366  return device->logicalDpiX();
367  }
368  }
369  return -1;
370 }
371 
372 void QgsDiagramRendererV2::_readXML( const QDomElement& elem, const QgsVectorLayer* layer )
373 {
374  Q_UNUSED( layer )
375 
376  delete mDiagram;
377  QString diagramType = elem.attribute( "diagramType" );
378  if ( diagramType == "Pie" )
379  {
380  mDiagram = new QgsPieDiagram();
381  }
382  else if ( diagramType == "Text" )
383  {
384  mDiagram = new QgsTextDiagram();
385  }
386  else if ( diagramType == "Histogram" )
387  {
389  }
390  else
391  {
392  mDiagram = 0;
393  }
394 }
395 
396 void QgsDiagramRendererV2::_writeXML( QDomElement& rendererElem, QDomDocument& doc, const QgsVectorLayer* layer ) const
397 {
398  Q_UNUSED( doc );
399  Q_UNUSED( layer )
400 
401  if ( mDiagram )
402  {
403  rendererElem.setAttribute( "diagramType", mDiagram->diagramName() );
404  }
405 }
406 
408 {
409 }
410 
412 {
413 }
414 
416 {
417  return new QgsSingleCategoryDiagramRenderer( *this );
418 }
419 
421 {
422  Q_UNUSED( c );
423  s = mSettings;
424  return true;
425 }
426 
428 {
429  return mDiagram->diagramSize( feature.attributes(), c, mSettings );
430 }
431 
432 QList<QgsDiagramSettings> QgsSingleCategoryDiagramRenderer::diagramSettings() const
433 {
434  QList<QgsDiagramSettings> settingsList;
435  settingsList.push_back( mSettings );
436  return settingsList;
437 }
438 
439 void QgsSingleCategoryDiagramRenderer::readXML( const QDomElement& elem, const QgsVectorLayer* layer )
440 {
441  QDomElement categoryElem = elem.firstChildElement( "DiagramCategory" );
442  if ( categoryElem.isNull() )
443  {
444  return;
445  }
446 
447  mSettings.readXML( categoryElem, layer );
448  _readXML( elem, layer );
449 }
450 
451 void QgsSingleCategoryDiagramRenderer::writeXML( QDomElement& layerElem, QDomDocument& doc, const QgsVectorLayer* layer ) const
452 {
453  QDomElement rendererElem = doc.createElement( "SingleCategoryDiagramRenderer" );
454  mSettings.writeXML( rendererElem, doc, layer );
455  _writeXML( rendererElem, doc, layer );
456  layerElem.appendChild( rendererElem );
457 }
458 
459 
461 {
462  mInterpolationSettings.classificationAttributeIsExpression = false;
463 }
464 
466 {
467 }
468 
470 {
471  return new QgsLinearlyInterpolatedDiagramRenderer( *this );
472 }
473 
475 {
476  QList<QgsDiagramSettings> settingsList;
477  settingsList.push_back( mSettings );
478  return settingsList;
479 }
480 
482 {
483  s = mSettings;
484  s.size = diagramSize( feature, c );
485  return true;
486 }
487 
489 {
490  return mSettings.categoryAttributes;
491 }
492 
494 {
495  return mDiagram->diagramSize( feature, c, mSettings, mInterpolationSettings );
496 }
497 
498 void QgsLinearlyInterpolatedDiagramRenderer::readXML( const QDomElement& elem, const QgsVectorLayer* layer )
499 {
500  mInterpolationSettings.lowerValue = elem.attribute( "lowerValue" ).toDouble();
501  mInterpolationSettings.upperValue = elem.attribute( "upperValue" ).toDouble();
502  mInterpolationSettings.lowerSize.setWidth( elem.attribute( "lowerWidth" ).toDouble() );
503  mInterpolationSettings.lowerSize.setHeight( elem.attribute( "lowerHeight" ).toDouble() );
504  mInterpolationSettings.upperSize.setWidth( elem.attribute( "upperWidth" ).toDouble() );
505  mInterpolationSettings.upperSize.setHeight( elem.attribute( "upperHeight" ).toDouble() );
506  mInterpolationSettings.classificationAttributeIsExpression = elem.hasAttribute( "classificationAttributeExpression" );
507  if ( mInterpolationSettings.classificationAttributeIsExpression )
508  {
509  mInterpolationSettings.classificationAttributeExpression = elem.attribute( "classificationAttributeExpression" );
510  }
511  else
512  {
513  mInterpolationSettings.classificationAttribute = elem.attribute( "classificationAttribute" ).toInt();
514  }
515  QDomElement settingsElem = elem.firstChildElement( "DiagramCategory" );
516  if ( !settingsElem.isNull() )
517  {
518  mSettings.readXML( settingsElem, layer );
519  }
520  _readXML( elem, layer );
521 }
522 
523 void QgsLinearlyInterpolatedDiagramRenderer::writeXML( QDomElement& layerElem, QDomDocument& doc, const QgsVectorLayer* layer ) const
524 {
525  QDomElement rendererElem = doc.createElement( "LinearlyInterpolatedDiagramRenderer" );
526  rendererElem.setAttribute( "lowerValue", QString::number( mInterpolationSettings.lowerValue ) );
527  rendererElem.setAttribute( "upperValue", QString::number( mInterpolationSettings.upperValue ) );
528  rendererElem.setAttribute( "lowerWidth", QString::number( mInterpolationSettings.lowerSize.width() ) );
529  rendererElem.setAttribute( "lowerHeight", QString::number( mInterpolationSettings.lowerSize.height() ) );
530  rendererElem.setAttribute( "upperWidth", QString::number( mInterpolationSettings.upperSize.width() ) );
531  rendererElem.setAttribute( "upperHeight", QString::number( mInterpolationSettings.upperSize.height() ) );
532  if ( mInterpolationSettings.classificationAttributeIsExpression )
533  {
534  rendererElem.setAttribute( "classificationAttributeExpression", mInterpolationSettings.classificationAttributeExpression );
535  }
536  else
537  {
538  rendererElem.setAttribute( "classificationAttribute", mInterpolationSettings.classificationAttribute );
539  }
540  mSettings.writeXML( rendererElem, doc, layer );
541  _writeXML( rendererElem, doc, layer );
542  layerElem.appendChild( rendererElem );
543 }
544 
545 QList< QgsLayerTreeModelLegendNode* > QgsDiagramSettings::legendItems( QgsLayerTreeLayer* nodeLayer ) const
546 {
547  QList< QgsLayerTreeModelLegendNode * > list;
548  for ( int i = 0 ; i < categoryLabels.size(); ++i )
549  {
550  QPixmap pix( 16, 16 );
551  pix.fill( categoryColors[i] );
552  list << new QgsSimpleLegendNode( nodeLayer, categoryLabels[i], QIcon( pix ), 0, QString( "diagram_%1" ).arg( QString::number( i ) ) );
553  }
554  return list;
555 }
556 
557 QList< QgsLayerTreeModelLegendNode* > QgsDiagramRendererV2::legendItems( QgsLayerTreeLayer* ) const
558 {
559  return QList< QgsLayerTreeModelLegendNode * >();
560 }
561 
562 QList< QgsLayerTreeModelLegendNode* > QgsSingleCategoryDiagramRenderer::legendItems( QgsLayerTreeLayer* nodeLayer ) const
563 {
564  return mSettings.legendItems( nodeLayer );
565 }
566 
567 QList< QgsLayerTreeModelLegendNode* > QgsLinearlyInterpolatedDiagramRenderer::legendItems( QgsLayerTreeLayer* nodeLayer ) const
568 {
569  return mSettings.legendItems( nodeLayer );
570 }
double minimumSize
Scale diagrams smaller than mMinimumSize to mMinimumSize.
void writeXML(QDomElement &layerElem, QDomDocument &doc, const QgsVectorLayer *layer) const override
QgsDiagramRendererV2 * clone() const override
Returns new instance that is equivalent to this one.
void _readXML(const QDomElement &elem, const QgsVectorLayer *layer)
virtual QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
QList< QString > categoryAttributes
virtual QList< QgsDiagramSettings > diagramSettings() const =0
Returns list with all diagram settings in the renderer.
static int dpiPaintDevice(const QPainter *)
Returns the paint device dpi (or -1 in case of error.
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QPointF &pos)
double scaleFactor() const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:119
virtual Q_DECL_DEPRECATED void renderDiagram(const QgsAttributes &att, QgsRenderContext &c, const QgsDiagramSettings &s, const QPointF &position)
Definition: qgsdiagram.cpp:110
void readXML(const QDomElement &elem, const QgsVectorLayer *layer)
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
Returns diagram settings for a feature.
virtual QSizeF diagramSize(const QgsFeature &features, const QgsRenderContext &c)=0
Returns size of the diagram (in painter units) or an invalid size in case of error.
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
void convertSizeToMapUnits(QSizeF &size, const QgsRenderContext &context) const
Converts size from mm to map units.
DiagramOrientation diagramOrientation
void readXML(const QDomElement &elem, const QgsVectorLayer *layer) override
virtual QSizeF sizeMapUnits(const QgsFeature &feature, const QgsRenderContext &c)
Returns size of the diagram for a feature in map units.
QgsAttributes attributes() const
Returns the feature's attributes.
Definition: qgsfeature.cpp:90
void readXML(const QDomElement &elem, const QgsVectorLayer *layer)
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
double mapUnitsPerPixel() const
Return current map units per pixel.
QgsDiagramRendererV2 * renderer
Implementation of legend node interface for displaying arbitrary label with icon. ...
virtual QSizeF diagramSize(const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s)=0
Returns the size in map units the diagram will use to render.
Base class for all diagram types.
Definition: qgsdiagram.h:34
virtual QString diagramName() const =0
void writeXML(QDomElement &layerElem, QDomDocument &doc, const QgsVectorLayer *layer) const override
void readXML(const QDomElement &elem, const QgsVectorLayer *layer) override
Contains information about the context of a rendering operation.
QList< QString > diagramAttributes() const override
Returns attribute indices needed for diagram rendering.
void writeXML(QDomElement &layerElem, QDomDocument &doc, const QgsVectorLayer *layer) const
QgsDiagramRendererV2 * clone() const override
Returns new instance that is equivalent to this one.
const QgsMapToPixel & mapToPixel() const
QList< QString > categoryLabels
int classificationAttribute
Index of the classification attribute.
void setDiagram(QgsDiagram *d)
QSizeF diagramSize(const QgsFeature &, const QgsRenderContext &c) override
Returns size of the diagram (in painter units) or an invalid size in case of error.
Represents a vector layer which manages a vector based data sets.
double size
Definition: qgssvgcache.cpp:77
QList< QColor > categoryColors
void writeXML(QDomElement &rendererElem, QDomDocument &doc, const QgsVectorLayer *layer) const
QgsDiagram * mDiagram
Reference to the object that does the real diagram rendering.
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
void _writeXML(QDomElement &rendererElem, QDomDocument &doc, const QgsVectorLayer *layer) const
LabelPlacementMethod labelPlacementMethod
QSizeF diagramSize(const QgsFeature &, const QgsRenderContext &c) override
Returns size of the diagram (in painter units) or an invalid size in case of error.
Layer tree node points to a map layer.