QGIS API Documentation  2.8.2-Wien
 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  QgsRectangle requestExtent = mContext.extent();
131  mRendererV2->modifyRequestExtent( requestExtent, mContext );
132 
133  QgsFeatureRequest featureRequest = QgsFeatureRequest()
134  .setFilterRect( requestExtent )
136 
137  // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine.
138  if ( mSimplifyGeometry )
139  {
140  double map2pixelTol = mSimplifyMethod.threshold();
141  bool validTransform = true;
142 
143  const QgsMapToPixel& mtp = mContext.mapToPixel();
144  map2pixelTol *= mtp.mapUnitsPerPixel();
146 
147  // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem
148  if ( ct && !(( QgsCoordinateTransform* )ct )->isShortCircuited() )
149  {
150  try
151  {
152  QgsPoint center = mContext.extent().center();
153  double rectSize = ct->sourceCrs().geographicFlag() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100;
154 
155  QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize );
156  QgsRectangle targetRect = ct->transform( sourceRect );
157 
158  QgsDebugMsg( QString( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ) );
159  QgsDebugMsg( QString( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ) );
160 
161  if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() )
162  {
163  QgsPoint minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() );
164  QgsPoint maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() );
165  QgsPoint minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() );
166  QgsPoint maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() );
167 
168  double sourceHypothenuse = sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) );
169  double targetHypothenuse = sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) );
170 
171  QgsDebugMsg( QString( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ) );
172  QgsDebugMsg( QString( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ) );
173 
174  if ( targetHypothenuse != 0 )
175  map2pixelTol *= ( sourceHypothenuse / targetHypothenuse );
176  }
177  }
178  catch ( QgsCsException &cse )
179  {
180  QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
181  validTransform = false;
182  }
183  }
184 
185  if ( validTransform )
186  {
187  QgsSimplifyMethod simplifyMethod;
189  simplifyMethod.setTolerance( map2pixelTol );
191 
192  featureRequest.setSimplifyMethod( simplifyMethod );
193 
195  mContext.setVectorSimplifyMethod( vectorMethod );
196  }
197  else
198  {
199  QgsVectorSimplifyMethod vectorMethod;
201  mContext.setVectorSimplifyMethod( vectorMethod );
202  }
203  }
204  else
205  {
206  QgsVectorSimplifyMethod vectorMethod;
208  mContext.setVectorSimplifyMethod( vectorMethod );
209  }
210 
211  QgsFeatureIterator fit = mSource->getFeatures( featureRequest );
212 
214  drawRendererV2Levels( fit );
215  else
216  drawRendererV2( fit );
217 
218  //apply layer transparency for vector layers
220  {
221  // a layer transparency has been set, so update the alpha for the flattened layer
222  // by combining it with the layer transparency
223  QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * mLayerTransparency / 100 ) );
224  // use destination in composition mode to merge source's alpha with destination
225  mContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn );
226  mContext.painter()->fillRect( 0, 0, mContext.painter()->device()->width(),
227  mContext.painter()->device()->height(), transparentFillColor );
228  }
229 
230  return true;
231 }
232 
234 {
235  mCache = cache;
236 
237  if ( mCache )
238  {
239  // Destroy all cached geometries and clear the references to them
241  }
242 }
243 
244 
245 
246 void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit )
247 {
248  QgsFeature fet;
249  while ( fit.nextFeature( fet ) )
250  {
251  try
252  {
253  if ( !fet.geometry() )
254  continue; // skip features without geometry
255 
256  if ( mContext.renderingStopped() )
257  {
258  QgsDebugMsg( QString( "Drawing of vector layer %1 cancelled." ).arg( layerID() ) );
259  break;
260  }
261 
262  bool sel = mContext.showSelection() && mSelectedFeatureIds.contains( fet.id() );
263  bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) );
264 
265  // render feature
266  bool rendered = mRendererV2->renderFeature( fet, mContext, -1, sel, drawMarker );
267 
268  if ( mCache )
269  {
270  // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry.
271  mCache->cacheGeometry( fet.id(), *fet.geometry() );
272  }
273 
274  // labeling - register feature
275  Q_UNUSED( rendered );
276  if ( rendered && mContext.labelingEngine() )
277  {
278  if ( mLabeling )
279  {
281  }
282  if ( mDiagrams )
283  {
285  }
286  }
287  }
288  catch ( const QgsCsException &cse )
289  {
290  Q_UNUSED( cse );
291  QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
292  .arg( fet.id() ).arg( cse.what() ) );
293  }
294  }
295 
296  stopRendererV2( NULL );
297 }
298 
299 void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
300 {
301  QHash< QgsSymbolV2*, QList<QgsFeature> > features; // key = symbol, value = array of features
302 
303  QgsSingleSymbolRendererV2* selRenderer = NULL;
304  if ( !mSelectedFeatureIds.isEmpty() )
305  {
307  selRenderer->symbol()->setColor( mContext.selectionColor() );
309  selRenderer->startRender( mContext, mFields );
310  }
311 
312  // 1. fetch features
313  QgsFeature fet;
314  while ( fit.nextFeature( fet ) )
315  {
316  if ( !fet.geometry() )
317  continue; // skip features without geometry
318 
319  if ( mContext.renderingStopped() )
320  {
321  qDebug( "rendering stop!" );
322  stopRendererV2( selRenderer );
323  return;
324  }
325 
327  if ( !sym )
328  {
329  continue;
330  }
331 
332  if ( !features.contains( sym ) )
333  {
334  features.insert( sym, QList<QgsFeature>() );
335  }
336  features[sym].append( fet );
337 
338  if ( mCache )
339  {
340  // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry.
341  mCache->cacheGeometry( fet.id(), *fet.geometry() );
342  }
343 
344  if ( mContext.labelingEngine() )
345  {
346  if ( mLabeling )
347  {
349  }
350  if ( mDiagrams )
351  {
353  }
354  }
355  }
356 
357  // find out the order
358  QgsSymbolV2LevelOrder levels;
359  QgsSymbolV2List symbols = mRendererV2->symbols();
360  for ( int i = 0; i < symbols.count(); i++ )
361  {
362  QgsSymbolV2* sym = symbols[i];
363  for ( int j = 0; j < sym->symbolLayerCount(); j++ )
364  {
365  int level = sym->symbolLayer( j )->renderingPass();
366  if ( level < 0 || level >= 1000 ) // ignore invalid levels
367  continue;
368  QgsSymbolV2LevelItem item( sym, j );
369  while ( level >= levels.count() ) // append new empty levels
370  levels.append( QgsSymbolV2Level() );
371  levels[level].append( item );
372  }
373  }
374 
375  // 2. draw features in correct order
376  for ( int l = 0; l < levels.count(); l++ )
377  {
378  QgsSymbolV2Level& level = levels[l];
379  for ( int i = 0; i < level.count(); i++ )
380  {
381  QgsSymbolV2LevelItem& item = level[i];
382  if ( !features.contains( item.symbol() ) )
383  {
384  QgsDebugMsg( "level item's symbol not found!" );
385  continue;
386  }
387  int layer = item.layer();
388  QList<QgsFeature>& lst = features[item.symbol()];
389  QList<QgsFeature>::iterator fit;
390  for ( fit = lst.begin(); fit != lst.end(); ++fit )
391  {
392  if ( mContext.renderingStopped() )
393  {
394  stopRendererV2( selRenderer );
395  return;
396  }
397 
398  bool sel = mSelectedFeatureIds.contains( fit->id() );
399  // maybe vertex markers should be drawn only during the last pass...
400  bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) );
401 
402  try
403  {
404  mRendererV2->renderFeature( *fit, mContext, layer, sel, drawMarker );
405  }
406  catch ( const QgsCsException &cse )
407  {
408  Q_UNUSED( cse );
409  QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
410  .arg( fet.id() ).arg( cse.what() ) );
411  }
412  }
413  }
414  }
415 
416  stopRendererV2( selRenderer );
417 }
418 
419 
420 void QgsVectorLayerRenderer::stopRendererV2( QgsSingleSymbolRendererV2* selRenderer )
421 {
423  if ( selRenderer )
424  {
425  selRenderer->stopRender( mContext );
426  delete selRenderer;
427  }
428 }
429 
430 
431 
432 
433 void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList& attributeNames )
434 {
435  if ( !mContext.labelingEngine() )
436  return;
437 
438  if ( mContext.labelingEngine()->prepareLayer( layer, attributeNames, mContext ) )
439  {
440  mLabeling = true;
441 
443  Q_UNUSED( palyr );
444 
445 #if 0 // TODO: limit of labels, font not found
446  // see if feature count limit is set for labeling
447  if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 )
448  {
449  QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
450  .setFilterRect( mContext.extent() )
451  .setSubsetOfAttributes( QgsAttributeList() ) );
452 
453  // total number of features that may be labeled
454  QgsFeature f;
455  int nFeatsToLabel = 0;
456  while ( fit.nextFeature( f ) )
457  {
458  nFeatsToLabel++;
459  }
460  palyr.mFeaturesToLabel = nFeatsToLabel;
461  }
462 
463  // notify user about any font substitution
464  if ( !palyr.mTextFontFound && !mLabelFontNotFoundNotified )
465  {
466  emit labelingFontNotFound( this, palyr.mTextFontFamily );
467  mLabelFontNotFoundNotified = true;
468  }
469 #endif
470  }
471 }
472 
473 void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QStringList& attributeNames )
474 {
475  if ( !mContext.labelingEngine() )
476  return;
477 
478  if ( !layer->diagramRenderer() || !layer->diagramLayerSettings() )
479  return;
480 
481  mDiagrams = true;
482 
483  const QgsDiagramRendererV2* diagRenderer = layer->diagramRenderer();
484  const QgsDiagramLayerSettings* diagSettings = layer->diagramLayerSettings();
485 
486  mContext.labelingEngine()->addDiagramLayer( layer, diagSettings ); // will make internal copy of diagSettings + initialize it
487 
488  //add attributes needed by the diagram renderer
489  QList<QString> att = diagRenderer->diagramAttributes();
490  QList<QString>::const_iterator attIt = att.constBegin();
491  for ( ; attIt != att.constEnd(); ++attIt )
492  {
493  QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, &mFields );
494  QStringList columns = expression->referencedColumns();
495  QStringList::const_iterator columnsIterator = columns.constBegin();
496  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
497  {
498  if ( !attributeNames.contains( *columnsIterator ) )
499  attributeNames << *columnsIterator;
500  }
501  }
502 
503  const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer*>( layer->diagramRenderer() );
504  if ( linearlyInterpolatedDiagramRenderer != NULL )
505  {
506  if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() )
507  {
508  QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), &mFields );
509  QStringList columns = expression->referencedColumns();
510  QStringList::const_iterator columnsIterator = columns.constBegin();
511  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
512  {
513  if ( !attributeNames.contains( *columnsIterator ) )
514  attributeNames << *columnsIterator;
515  }
516  }
517  else
518  {
519  QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name();
520  if ( !attributeNames.contains( name ) )
521  attributeNames << name;
522  }
523  }
524 
525  //and the ones needed for data defined diagram positions
526  if ( diagSettings->xPosColumn != -1 )
527  attributeNames << mFields.at( diagSettings->xPosColumn ).name();
528  if ( diagSettings->yPosColumn != -1 )
529  attributeNames << mFields.at( diagSettings->yPosColumn ).name();
530 }