QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsvectorlayerrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerrenderer.cpp
3  --------------------------------------
4  Date : December 2013
5  Copyright : (C) 2013 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
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 
16 #include "qgsvectorlayerrenderer.h"
17 
18 //#include "qgsfeatureiterator.h"
19 #include "diagram/qgsdiagram.h"
20 #include "qgsdiagramrendererv2.h"
21 #include "qgsgeometrycache.h"
22 #include "qgsmessagelog.h"
23 #include "qgspallabeling.h"
24 #include "qgsrendererv2.h"
25 #include "qgsrendercontext.h"
27 #include "qgssymbollayerv2.h"
28 #include "qgssymbolv2.h"
29 #include "qgsvectorlayer.h"
31 
32 #include <QSettings>
33 
34 // TODO:
35 // - passing of cache to QgsVectorLayer
36 
37 
39  : QgsMapLayerRenderer( layer->id() )
40  , mContext( context )
41  , mFields( layer->pendingFields() )
42  , mRendererV2( 0 )
43  , mCache( 0 )
44  , mLabeling( false )
45  , mDiagrams( false )
46  , mLayerTransparency( 0 )
47 {
48  mSource = new QgsVectorLayerFeatureSource( layer );
49 
50  mRendererV2 = layer->rendererV2() ? layer->rendererV2()->clone() : 0;
52 
53  mDrawVertexMarkers = ( layer->editBuffer() != 0 );
54 
55  mGeometryType = layer->geometryType();
56 
59 
62 
63  QSettings settings;
64  mVertexMarkerOnlyForSelection = settings.value( "/qgis/digitizing/marker_only_for_selected", false ).toBool();
65 
66  QString markerTypeString = settings.value( "/qgis/digitizing/marker_style", "Cross" ).toString();
67  if ( markerTypeString == "Cross" )
68  {
70  }
71  else if ( markerTypeString == "SemiTransparentCircle" )
72  {
74  }
75  else
76  {
78  }
79 
80  mVertexMarkerSize = settings.value( "/qgis/digitizing/marker_size", 3 ).toInt();
81 
82  if ( !mRendererV2 )
83  return;
84 
85  QgsDebugMsg( "rendering v2:\n " + mRendererV2->dump() );
86 
87  if ( mDrawVertexMarkers )
88  {
89  // set editing vertex markers style
91  }
92 
94 
95  //register label and diagram layer to the labeling engine
96  prepareLabeling( layer, mAttrNames );
97  prepareDiagrams( layer, mAttrNames );
98 
99 }
100 
101 
103 {
104  delete mRendererV2;
105  delete mSource;
106 }
107 
108 
110 {
112  return true;
113 
114  if ( !mRendererV2 )
115  {
116  mErrors.append( QObject::tr( "No renderer for drawing." ) );
117  return false;
118  }
119 
120  // Per feature blending mode
121  if ( mContext.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver )
122  {
123  // set the painter to the feature blend mode, so that features drawn
124  // on this layer will interact and blend with each other
125  mContext.painter()->setCompositionMode( mFeatureBlendMode );
126  }
127 
129 
130  QgsFeatureRequest featureRequest = QgsFeatureRequest()
132  .setSubsetOfAttributes( mAttrNames, mFields );
133 
134  // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine.
135  if ( mSimplifyGeometry )
136  {
137  double map2pixelTol = mSimplifyMethod.threshold();
138 
139  const QgsMapToPixel& mtp = mContext.mapToPixel();
140  map2pixelTol *= mtp.mapUnitsPerPixel();
142 
143  // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem
144  if ( ct && !(( QgsCoordinateTransform* )ct )->isShortCircuited() )
145  {
146  try
147  {
148  QgsPoint center = mContext.extent().center();
149  double rectSize = ct->sourceCrs().geographicFlag() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100;
150 
151  QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize );
152  QgsRectangle targetRect = ct->transform( sourceRect );
153 
154  QgsDebugMsg( QString( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ) );
155  QgsDebugMsg( QString( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ) );
156 
157  if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() )
158  {
159  QgsPoint minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() );
160  QgsPoint maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() );
161  QgsPoint minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() );
162  QgsPoint maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() );
163 
164  double sourceHypothenuse = sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) );
165  double targetHypothenuse = sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) );
166 
167  QgsDebugMsg( QString( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ) );
168  QgsDebugMsg( QString( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ) );
169 
170  if ( targetHypothenuse != 0 )
171  map2pixelTol *= ( sourceHypothenuse / targetHypothenuse );
172  }
173  }
174  catch ( QgsCsException &cse )
175  {
176  QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
177  }
178  }
179 
180  QgsSimplifyMethod simplifyMethod;
182  simplifyMethod.setTolerance( map2pixelTol );
184 
185  featureRequest.setSimplifyMethod( simplifyMethod );
186 
188  mContext.setVectorSimplifyMethod( vectorMethod );
189  }
190  else
191  {
192  QgsVectorSimplifyMethod vectorMethod;
194  mContext.setVectorSimplifyMethod( vectorMethod );
195  }
196 
197  QgsFeatureIterator fit = mSource->getFeatures( featureRequest );
198 
200  drawRendererV2Levels( fit );
201  else
202  drawRendererV2( fit );
203 
204  //apply layer transparency for vector layers
206  {
207  // a layer transparency has been set, so update the alpha for the flattened layer
208  // by combining it with the layer transparency
209  QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * mLayerTransparency / 100 ) );
210  // use destination in composition mode to merge source's alpha with destination
211  mContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn );
212  mContext.painter()->fillRect( 0, 0, mContext.painter()->device()->width(),
213  mContext.painter()->device()->height(), transparentFillColor );
214  }
215 
216  return true;
217 }
218 
220 {
221  mCache = cache;
222 
223  if ( mCache )
224  {
225  // Destroy all cached geometries and clear the references to them
227  }
228 }
229 
230 
231 
233 {
234  QgsFeature fet;
235  while ( fit.nextFeature( fet ) )
236  {
237  try
238  {
239  if ( !fet.geometry() )
240  continue; // skip features without geometry
241 
242  if ( mContext.renderingStopped() )
243  {
244  QgsDebugMsg( QString( "Drawing of vector layer %1 cancelled." ).arg( layerID() ) );
245  break;
246  }
247 
248  bool sel = mContext.showSelection() && mSelectedFeatureIds.contains( fet.id() );
249  bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) );
250 
251  // render feature
252  bool rendered = mRendererV2->renderFeature( fet, mContext, -1, sel, drawMarker );
253 
254  if ( mCache )
255  {
256  // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry.
257  mCache->cacheGeometry( fet.id(), *fet.geometry() );
258  }
259 
260  // labeling - register feature
261  Q_UNUSED( rendered );
262  if ( rendered && mContext.labelingEngine() )
263  {
264  if ( mLabeling )
265  {
267  }
268  if ( mDiagrams )
269  {
271  }
272  }
273  }
274  catch ( const QgsCsException &cse )
275  {
276  Q_UNUSED( cse );
277  QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
278  .arg( fet.id() ).arg( cse.what() ) );
279  }
280  }
281 
282  stopRendererV2( NULL );
283 }
284 
286 {
287  QHash< QgsSymbolV2*, QList<QgsFeature> > features; // key = symbol, value = array of features
288 
289  QgsSingleSymbolRendererV2* selRenderer = NULL;
290  if ( !mSelectedFeatureIds.isEmpty() )
291  {
293  selRenderer->symbol()->setColor( mContext.selectionColor() );
295  selRenderer->startRender( mContext, mFields );
296  }
297 
298  // 1. fetch features
299  QgsFeature fet;
300  while ( fit.nextFeature( fet ) )
301  {
302  if ( !fet.geometry() )
303  continue; // skip features without geometry
304 
305  if ( mContext.renderingStopped() )
306  {
307  qDebug( "rendering stop!" );
308  stopRendererV2( selRenderer );
309  return;
310  }
311 
313  if ( !sym )
314  {
315  continue;
316  }
317 
318  if ( !features.contains( sym ) )
319  {
320  features.insert( sym, QList<QgsFeature>() );
321  }
322  features[sym].append( fet );
323 
324  if ( mCache )
325  {
326  // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry.
327  mCache->cacheGeometry( fet.id(), *fet.geometry() );
328  }
329 
330  if ( sym && mContext.labelingEngine() )
331  {
332  if ( mLabeling )
333  {
335  }
336  if ( mDiagrams )
337  {
339  }
340  }
341  }
342 
343  // find out the order
344  QgsSymbolV2LevelOrder levels;
345  QgsSymbolV2List symbols = mRendererV2->symbols();
346  for ( int i = 0; i < symbols.count(); i++ )
347  {
348  QgsSymbolV2* sym = symbols[i];
349  for ( int j = 0; j < sym->symbolLayerCount(); j++ )
350  {
351  int level = sym->symbolLayer( j )->renderingPass();
352  if ( level < 0 || level >= 1000 ) // ignore invalid levels
353  continue;
354  QgsSymbolV2LevelItem item( sym, j );
355  while ( level >= levels.count() ) // append new empty levels
356  levels.append( QgsSymbolV2Level() );
357  levels[level].append( item );
358  }
359  }
360 
361  // 2. draw features in correct order
362  for ( int l = 0; l < levels.count(); l++ )
363  {
364  QgsSymbolV2Level& level = levels[l];
365  for ( int i = 0; i < level.count(); i++ )
366  {
367  QgsSymbolV2LevelItem& item = level[i];
368  if ( !features.contains( item.symbol() ) )
369  {
370  QgsDebugMsg( "level item's symbol not found!" );
371  continue;
372  }
373  int layer = item.layer();
374  QList<QgsFeature>& lst = features[item.symbol()];
375  QList<QgsFeature>::iterator fit;
376  for ( fit = lst.begin(); fit != lst.end(); ++fit )
377  {
378  if ( mContext.renderingStopped() )
379  {
380  stopRendererV2( selRenderer );
381  return;
382  }
383 
384  bool sel = mSelectedFeatureIds.contains( fit->id() );
385  // maybe vertex markers should be drawn only during the last pass...
386  bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) );
387 
388  try
389  {
390  mRendererV2->renderFeature( *fit, mContext, layer, sel, drawMarker );
391  }
392  catch ( const QgsCsException &cse )
393  {
394  Q_UNUSED( cse );
395  QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
396  .arg( fet.id() ).arg( cse.what() ) );
397  }
398  }
399  }
400  }
401 
402  stopRendererV2( selRenderer );
403 }
404 
405 
407 {
409  if ( selRenderer )
410  {
411  selRenderer->stopRender( mContext );
412  delete selRenderer;
413  }
414 }
415 
416 
417 
418 
419 void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList& attributeNames )
420 {
421  if ( !mContext.labelingEngine() )
422  return;
423 
424  if ( mContext.labelingEngine()->prepareLayer( layer, attributeNames, mContext ) )
425  {
426  mLabeling = true;
427 
429  Q_UNUSED( palyr );
430 
431 #if 0 // TODO: limit of labels, font not found
432  // see if feature count limit is set for labeling
433  if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 )
434  {
435  QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
436  .setFilterRect( mContext.extent() )
437  .setSubsetOfAttributes( QgsAttributeList() ) );
438 
439  // total number of features that may be labeled
440  QgsFeature f;
441  int nFeatsToLabel = 0;
442  while ( fit.nextFeature( f ) )
443  {
444  nFeatsToLabel++;
445  }
446  palyr.mFeaturesToLabel = nFeatsToLabel;
447  }
448 
449  // notify user about any font substitution
450  if ( !palyr.mTextFontFound && !mLabelFontNotFoundNotified )
451  {
452  emit labelingFontNotFound( this, palyr.mTextFontFamily );
453  mLabelFontNotFoundNotified = true;
454  }
455 #endif
456  }
457 }
458 
459 void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QStringList& attributeNames )
460 {
461  if ( !mContext.labelingEngine() )
462  return;
463 
464  if ( !layer->diagramRenderer() || !layer->diagramLayerSettings() )
465  return;
466 
467  mDiagrams = true;
468 
469  const QgsDiagramRendererV2* diagRenderer = layer->diagramRenderer();
470  const QgsDiagramLayerSettings* diagSettings = layer->diagramLayerSettings();
471 
472  mContext.labelingEngine()->addDiagramLayer( layer, diagSettings ); // will make internal copy of diagSettings + initialize it
473 
474  //add attributes needed by the diagram renderer
475  QList<QString> att = diagRenderer->diagramAttributes();
476  QList<QString>::const_iterator attIt = att.constBegin();
477  for ( ; attIt != att.constEnd(); ++attIt )
478  {
479  QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, &mFields );
480  QStringList columns = expression->referencedColumns();
481  QStringList::const_iterator columnsIterator = columns.constBegin();
482  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
483  {
484  if ( !attributeNames.contains( *columnsIterator ) )
485  attributeNames << *columnsIterator;
486  }
487  }
488 
489  const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer*>( layer->diagramRenderer() );
490  if ( linearlyInterpolatedDiagramRenderer != NULL )
491  {
492  if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() )
493  {
494  QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), &mFields );
495  QStringList columns = expression->referencedColumns();
496  QStringList::const_iterator columnsIterator = columns.constBegin();
497  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
498  {
499  if ( !attributeNames.contains( *columnsIterator ) )
500  attributeNames << *columnsIterator;
501  }
502  }
503  else
504  {
505  QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name();
506  if ( !attributeNames.contains( name ) )
507  attributeNames << name;
508  }
509  }
510 
511  //and the ones needed for data defined diagram positions
512  if ( diagSettings->xPosColumn != -1 )
513  attributeNames << mFields.at( diagSettings->xPosColumn ).name();
514  if ( diagSettings->yPosColumn != -1 )
515  attributeNames << mFields.at( diagSettings->yPosColumn ).name();
516 }