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