QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrendererv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 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 "qgsrendererv2.h"
17 #include "qgssymbolv2.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsrulebasedrendererv2.h"
20 
21 #include "qgssinglesymbolrendererv2.h" // for default renderer
22 
23 #include "qgsrendererv2registry.h"
24 
25 #include "qgsrendercontext.h"
26 #include "qgsclipper.h"
27 #include "qgsgeometry.h"
28 #include "qgsfeature.h"
29 #include "qgslogger.h"
30 #include "qgsvectorlayer.h"
31 
32 #include <QDomElement>
33 #include <QDomDocument>
34 #include <QPolygonF>
35 
36 
37 
38 const unsigned char* QgsFeatureRendererV2::_getPoint( QPointF& pt, QgsRenderContext& context, const unsigned char* wkb )
39 {
40  QgsConstWkbPtr wkbPtr( wkb + 1 );
41  unsigned int wkbType;
42  wkbPtr >> wkbType;
43 
44  double x, y;
45  wkbPtr >> x >> y;
46 
47  if ( wkbType == QGis::WKBPoint25D )
48  wkbPtr += sizeof( double );
49 
50  if ( context.coordinateTransform() )
51  {
52  double z = 0; // dummy variable for coordiante transform
53  context.coordinateTransform()->transformInPlace( x, y, z );
54  }
55 
56  context.mapToPixel().transformInPlace( x, y );
57 
58  pt = QPointF( x, y );
59  return wkbPtr;
60 }
61 
62 const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb )
63 {
64  QgsConstWkbPtr wkbPtr( wkb );
65  unsigned int wkbType, nPoints;
66  wkbPtr >> wkbType >> nPoints;
67 
68  bool hasZValue = ( wkbType == QGis::WKBLineString25D );
69 
70  double x, y;
71  const QgsCoordinateTransform* ct = context.coordinateTransform();
72  const QgsMapToPixel& mtp = context.mapToPixel();
73 
74  //apply clipping for large lines to achieve a better rendering performance
75  if ( nPoints > 1 )
76  {
77  const QgsRectangle& e = context.extent();
78  double cw = e.width() / 10; double ch = e.height() / 10;
79  QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
80  wkbPtr = QgsConstWkbPtr( QgsClipper::clippedLineWKB( wkb, clipRect, pts ) );
81  }
82  else
83  {
84  pts.resize( nPoints );
85 
86  QPointF* ptr = pts.data();
87  for ( unsigned int i = 0; i < nPoints; ++i, ++ptr )
88  {
89  wkbPtr >> x >> y;
90  if ( hasZValue )
91  wkbPtr += sizeof( double );
92 
93  *ptr = QPointF( x, y );
94  }
95  }
96 
97  //transform the QPolygonF to screen coordinates
98  if ( ct )
99  {
100  ct->transformPolygon( pts );
101  }
102 
103  QPointF* ptr = pts.data();
104  for ( int i = 0; i < pts.size(); ++i, ++ptr )
105  {
106  mtp.transformInPlace( ptr->rx(), ptr->ry() );
107  }
108 
109  return wkbPtr;
110 }
111 
112 const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb )
113 {
114  QgsConstWkbPtr wkbPtr( wkb + 1 );
115 
116  unsigned int wkbType, numRings;
117  wkbPtr >> wkbType >> numRings;
118 
119  if ( numRings == 0 ) // sanity check for zero rings in polygon
120  return wkbPtr;
121 
122  bool hasZValue = ( wkbType == QGis::WKBPolygon25D );
123 
124  double x, y;
125  holes.clear();
126 
127  const QgsCoordinateTransform* ct = context.coordinateTransform();
128  const QgsMapToPixel& mtp = context.mapToPixel();
129  const QgsRectangle& e = context.extent();
130  double cw = e.width() / 10; double ch = e.height() / 10;
131  QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
132 
133  for ( unsigned int idx = 0; idx < numRings; idx++ )
134  {
135  unsigned int nPoints;
136  wkbPtr >> nPoints;
137 
138  QPolygonF poly( nPoints );
139 
140  // Extract the points from the WKB and store in a pair of vectors.
141  QPointF* ptr = poly.data();
142  for ( unsigned int jdx = 0; jdx < nPoints; ++jdx, ++ptr )
143  {
144  wkbPtr >> x >> y;
145  if ( hasZValue )
146  wkbPtr += sizeof( double );
147 
148  *ptr = QPointF( x, y );
149  }
150 
151  if ( nPoints < 1 )
152  continue;
153 
154  //clip close to view extent, if needed
155  QRectF ptsRect = poly.boundingRect();
156  if ( !context.extent().contains( ptsRect ) ) QgsClipper::trimPolygon( poly, clipRect );
157 
158  //transform the QPolygonF to screen coordinates
159  if ( ct )
160  {
161  ct->transformPolygon( poly );
162  }
163 
164 
165  ptr = poly.data();
166  for ( int i = 0; i < poly.size(); ++i, ++ptr )
167  {
168  mtp.transformInPlace( ptr->rx(), ptr->ry() );
169  }
170 
171  if ( idx == 0 )
172  pts = poly;
173  else
174  holes.append( poly );
175  }
176 
177  return wkbPtr;
178 }
179 
181 {
182  if ( symbol )
183  {
184  if ( symbol->type() == QgsSymbolV2::Marker )
185  {
186  QgsMarkerSymbolV2* ms = static_cast<QgsMarkerSymbolV2*>( symbol );
187  if ( ms )
188  {
189  ms->setScaleMethod(( QgsSymbolV2::ScaleMethod )scaleMethod );
190  }
191  }
192  }
193 }
194 
195 
197  : mType( type ), mUsingSymbolLevels( false ),
198  mCurrentVertexMarkerType( QgsVectorLayer::Cross ),
199  mCurrentVertexMarkerSize( 3 )
200 {
201 }
202 
204 {
205  return new QgsSingleSymbolRendererV2( QgsSymbolV2::defaultSymbol( geomType ) );
206 }
207 
209 {
210  startRender( context, vlayer->pendingFields() );
211 }
212 
213 
214 bool QgsFeatureRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
215 {
216  QgsSymbolV2* symbol = symbolForFeature( feature );
217  if ( symbol == NULL )
218  return false;
219 
220  renderFeatureWithSymbol( feature, symbol, context, layer, selected, drawVertexMarker );
221  return true;
222 }
223 
224 void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymbolV2* symbol, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
225 {
226  QgsSymbolV2::SymbolType symbolType = symbol->type();
227 
228  QgsGeometry* geom = feature.geometry();
229  switch ( geom->wkbType() )
230  {
231  case QGis::WKBPoint:
232  case QGis::WKBPoint25D:
233  {
234  if ( symbolType != QgsSymbolV2::Marker )
235  {
236  QgsDebugMsg( "point can be drawn only with marker symbol!" );
237  break;
238  }
239  QPointF pt;
240  _getPoint( pt, context, geom->asWkb() );
241  (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
242 
243  //if ( drawVertexMarker )
244  // renderVertexMarker( pt, context );
245  }
246  break;
247 
248  case QGis::WKBLineString:
250  {
251  if ( symbolType != QgsSymbolV2::Line )
252  {
253  QgsDebugMsg( "linestring can be drawn only with line symbol!" );
254  break;
255  }
256  QPolygonF pts;
257  _getLineString( pts, context, geom->asWkb() );
258  (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );
259 
260  if ( drawVertexMarker )
261  renderVertexMarkerPolyline( pts, context );
262  }
263  break;
264 
265  case QGis::WKBPolygon:
266  case QGis::WKBPolygon25D:
267  {
268  if ( symbolType != QgsSymbolV2::Fill )
269  {
270  QgsDebugMsg( "polygon can be drawn only with fill symbol!" );
271  break;
272  }
273  QPolygonF pts;
274  QList<QPolygonF> holes;
275  _getPolygon( pts, holes, context, geom->asWkb() );
276  (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
277 
278  if ( drawVertexMarker )
279  renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
280  }
281  break;
282 
283  case QGis::WKBMultiPoint:
285  {
286  if ( symbolType != QgsSymbolV2::Marker )
287  {
288  QgsDebugMsg( "multi-point can be drawn only with marker symbol!" );
289  break;
290  }
291 
292  QgsConstWkbPtr wkbPtr( geom->asWkb() + 5 );
293  unsigned int num;
294  wkbPtr >> num;
295  const unsigned char* ptr = wkbPtr;
296  QPointF pt;
297 
298  for ( unsigned int i = 0; i < num; ++i )
299  {
300  ptr = QgsConstWkbPtr( _getPoint( pt, context, ptr ) );
301  (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
302 
303  //if ( drawVertexMarker )
304  // renderVertexMarker( pt, context );
305  }
306  }
307  break;
308 
311  {
312  if ( symbolType != QgsSymbolV2::Line )
313  {
314  QgsDebugMsg( "multi-linestring can be drawn only with line symbol!" );
315  break;
316  }
317 
318  QgsConstWkbPtr wkbPtr( geom->asWkb() + 5 );
319  unsigned int num;
320  wkbPtr >> num;
321  const unsigned char* ptr = wkbPtr;
322  QPolygonF pts;
323 
324  for ( unsigned int i = 0; i < num; ++i )
325  {
326  ptr = QgsConstWkbPtr( _getLineString( pts, context, ptr ) );
327  (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );
328 
329  if ( drawVertexMarker )
330  renderVertexMarkerPolyline( pts, context );
331  }
332  }
333  break;
334 
337  {
338  if ( symbolType != QgsSymbolV2::Fill )
339  {
340  QgsDebugMsg( "multi-polygon can be drawn only with fill symbol!" );
341  break;
342  }
343 
344  QgsConstWkbPtr wkbPtr( geom->asWkb() + 5 );
345  unsigned int num;
346  wkbPtr >> num;
347  const unsigned char* ptr = wkbPtr;
348  QPolygonF pts;
349  QList<QPolygonF> holes;
350 
351  for ( unsigned int i = 0; i < num; ++i )
352  {
353  ptr = _getPolygon( pts, holes, context, ptr );
354  (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
355 
356  if ( drawVertexMarker )
357  renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
358  }
359  }
360  break;
361 
362  default:
363  QgsDebugMsg( QString( "feature %1: unsupported wkb type 0x%2 for rendering" ).arg( feature.id() ).arg( geom->wkbType(), 0, 16 ) );
364  }
365 }
366 
368 {
369  return "UNKNOWN RENDERER\n";
370 }
371 
372 
374 {
375  // <renderer-v2 type=""> ... </renderer-v2>
376 
377  if ( element.isNull() )
378  return NULL;
379 
380  // load renderer
381  QString rendererType = element.attribute( "type" );
382 
384  if ( m == NULL )
385  return NULL;
386 
387  QgsFeatureRendererV2* r = m->createRenderer( element );
388  if ( r )
389  {
390  r->setUsingSymbolLevels( element.attribute( "symbollevels", "0" ).toInt() );
391  }
392  return r;
393 }
394 
395 QDomElement QgsFeatureRendererV2::save( QDomDocument& doc )
396 {
397  // create empty renderer element
398  return doc.createElement( RENDERER_TAG_NAME );
399 }
400 
401 QgsFeatureRendererV2* QgsFeatureRendererV2::loadSld( const QDomNode &node, QGis::GeometryType geomType, QString &errorMessage )
402 {
403  QDomElement element = node.toElement();
404  if ( element.isNull() )
405  return NULL;
406 
407  // get the UserStyle element
408  QDomElement userStyleElem = element.firstChildElement( "UserStyle" );
409  if ( userStyleElem.isNull() )
410  {
411  // UserStyle element not found, nothing will be rendered
412  errorMessage = "Info: UserStyle element not found.";
413  return NULL;
414  }
415 
416  // get the FeatureTypeStyle element
417  QDomElement featTypeStyleElem = userStyleElem.firstChildElement( "FeatureTypeStyle" );
418  if ( featTypeStyleElem.isNull() )
419  {
420  errorMessage = "Info: FeatureTypeStyle element not found.";
421  return NULL;
422  }
423 
424  // use the RuleRenderer when more rules are present or the rule
425  // has filters or min/max scale denominators set,
426  // otherwise use the SingleSymbol renderer
427  bool needRuleRenderer = false;
428  int ruleCount = 0;
429 
430  QDomElement ruleElem = featTypeStyleElem.firstChildElement( "Rule" );
431  while ( !ruleElem.isNull() )
432  {
433  ruleCount++;
434 
435  // more rules present, use the RuleRenderer
436  if ( ruleCount > 1 )
437  {
438  QgsDebugMsg( "more Rule elements found: need a RuleRenderer" );
439  needRuleRenderer = true;
440  break;
441  }
442 
443  QDomElement ruleChildElem = ruleElem.firstChildElement();
444  while ( !ruleChildElem.isNull() )
445  {
446  // rule has filter or min/max scale denominator, use the RuleRenderer
447  if ( ruleChildElem.localName() == "Filter" ||
448  ruleChildElem.localName() == "MinScaleDenominator" ||
449  ruleChildElem.localName() == "MaxScaleDenominator" )
450  {
451  QgsDebugMsg( "Filter or Min/MaxScaleDenominator element found: need a RuleRenderer" );
452  needRuleRenderer = true;
453  break;
454  }
455 
456  ruleChildElem = ruleChildElem.nextSiblingElement();
457  }
458 
459  if ( needRuleRenderer )
460  {
461  break;
462  }
463 
464  ruleElem = ruleElem.nextSiblingElement( "Rule" );
465  }
466 
467  QString rendererType;
468  if ( needRuleRenderer )
469  {
470  rendererType = "RuleRenderer";
471  }
472  else
473  {
474  rendererType = "singleSymbol";
475  }
476  QgsDebugMsg( QString( "Instantiating a '%1' renderer..." ).arg( rendererType ) );
477 
478  // create the renderer and return it
480  if ( m == NULL )
481  {
482  errorMessage = QString( "Error: Unable to get metadata for '%1' renderer." ).arg( rendererType );
483  return NULL;
484  }
485 
486  QgsFeatureRendererV2* r = m->createRendererFromSld( featTypeStyleElem, geomType );
487  return r;
488 }
489 
490 QDomElement QgsFeatureRendererV2::writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const
491 {
492  return writeSld( doc, layer.name() );
493 }
494 
495 QDomElement QgsFeatureRendererV2::writeSld( QDomDocument& doc, const QString& styleName ) const
496 {
497  QDomElement userStyleElem = doc.createElement( "UserStyle" );
498 
499  QDomElement nameElem = doc.createElement( "se:Name" );
500  nameElem.appendChild( doc.createTextNode( styleName ) );
501  userStyleElem.appendChild( nameElem );
502 
503  QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" );
504  toSld( doc, featureTypeStyleElem );
505  userStyleElem.appendChild( featureTypeStyleElem );
506 
507  return userStyleElem;
508 }
509 
511 {
512  Q_UNUSED( iconSize );
513  // empty list by default
514  return QgsLegendSymbologyList();
515 }
516 
518 {
519  return false;
520 }
521 
523 {
524  Q_UNUSED( key );
525  return false;
526 }
527 
528 void QgsFeatureRendererV2::checkLegendSymbolItem( QString key, bool state )
529 {
530  Q_UNUSED( key );
531  Q_UNUSED( state );
532 }
533 
534 QgsLegendSymbolList QgsFeatureRendererV2::legendSymbolItems( double scaleDenominator, QString rule )
535 {
536  Q_UNUSED( scaleDenominator );
537  Q_UNUSED( rule );
538  return QgsLegendSymbolList();
539 }
540 
542 {
543  QgsLegendSymbolList lst = const_cast<QgsFeatureRendererV2*>( this )->legendSymbolItems();
545  int i = 0;
546  for ( QgsLegendSymbolList::const_iterator it = lst.begin(); it != lst.end(); ++it, ++i )
547  {
548  lst2 << QgsLegendSymbolItemV2( it->second, it->first, QString::number( i ), legendSymbolItemsCheckable() );
549  }
550  return lst2;
551 }
552 
554 {
557 }
558 
560 {
561  QgsVectorLayer::drawVertexMarker( pt.x(), pt.y(), *context.painter(),
564 }
565 
567 {
568  foreach ( QPointF pt, pts )
569  renderVertexMarker( pt, context );
570 }
571 
572 void QgsFeatureRendererV2::renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context )
573 {
574  foreach ( QPointF pt, pts )
575  renderVertexMarker( pt, context );
576 
577  if ( rings )
578  {
579  foreach ( QPolygonF ring, *rings )
580  {
581  foreach ( QPointF pt, ring )
582  renderVertexMarker( pt, context );
583  }
584  }
585 }
586 
588 {
589  QgsSymbolV2List lst;
590  QgsSymbolV2* s = symbolForFeature( feat );
591  if ( s ) lst.append( s );
592  return lst;
593 }
594 
596 {
597  QgsSymbolV2List lst;
599  if ( s ) lst.append( s );
600  return lst;
601 }