QGIS API Documentation  2.2.0-Valmiera
 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  wkb++; // jump over endian info
40  unsigned int wkbType = *(( int* ) wkb );
41  wkb += sizeof( unsigned int );
42 
43  double x = *(( double * ) wkb ); wkb += sizeof( double );
44  double y = *(( double * ) wkb ); wkb += sizeof( double );
45 
46  if ( wkbType == QGis::WKBPolygon25D )
47  wkb += 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 wkb;
59 }
60 
61 const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb )
62 {
63  wkb++; // jump over endian info
64  unsigned int wkbType = *(( int* ) wkb );
65  wkb += sizeof( unsigned int );
66  unsigned int nPoints = *(( int* ) wkb );
67  wkb += sizeof( unsigned int );
68 
69  bool hasZValue = ( wkbType == QGis::WKBLineString25D );
70 
71  int sizeOfDoubleX = sizeof( double );
72  int sizeOfDoubleY = hasZValue ? 2 * sizeof( double ) : sizeof( double );
73 
74  double x, y;
75  const QgsCoordinateTransform* ct = context.coordinateTransform();
76  const QgsMapToPixel& mtp = context.mapToPixel();
77 
78  //apply clipping for large lines to achieve a better rendering performance
79  if ( nPoints > 1 )
80  {
81  const QgsRectangle& e = context.extent();
82  double cw = e.width() / 10; double ch = e.height() / 10;
83  QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
84  wkb = QgsClipper::clippedLineWKB( wkb - ( 2 * sizeof( unsigned int ) + 1 ), clipRect, pts );
85  }
86  else
87  {
88  pts.resize( nPoints );
89 
90  QPointF* ptr = pts.data();
91  for ( unsigned int i = 0; i < nPoints; ++i, ++ptr )
92  {
93  memcpy( &x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
94  memcpy( &y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;
95 
96  *ptr = QPointF( x, y );
97  }
98  }
99 
100  //transform the QPolygonF to screen coordinates
101  if ( ct )
102  {
103  ct->transformPolygon( pts );
104  }
105 
106  QPointF* ptr = pts.data();
107  for ( int i = 0; i < pts.size(); ++i, ++ptr )
108  {
109  mtp.transformInPlace( ptr->rx(), ptr->ry() );
110  }
111 
112 
113  return wkb;
114 }
115 
116 const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb )
117 {
118  wkb++; // jump over endian info
119  unsigned int wkbType = *(( int* ) wkb );
120  wkb += sizeof( unsigned int ); // jump over wkb type
121  unsigned int numRings = *(( int* ) wkb );
122  wkb += sizeof( unsigned int );
123 
124  if ( numRings == 0 ) // sanity check for zero rings in polygon
125  return wkb;
126 
127  bool hasZValue = ( wkbType == QGis::WKBPolygon25D );
128 
129  int sizeOfDoubleX = sizeof( double );
130  int sizeOfDoubleY = hasZValue ? 2 * sizeof( double ) : sizeof( double );
131 
132  double x, y;
133  holes.clear();
134 
135  const QgsCoordinateTransform* ct = context.coordinateTransform();
136  const QgsMapToPixel& mtp = context.mapToPixel();
137  const QgsRectangle& e = context.extent();
138  double cw = e.width() / 10; double ch = e.height() / 10;
139  QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
140 
141  for ( unsigned int idx = 0; idx < numRings; idx++ )
142  {
143  unsigned int nPoints = *(( int* )wkb );
144  wkb += sizeof( unsigned int );
145 
146  QPolygonF poly( nPoints );
147 
148  // Extract the points from the WKB and store in a pair of vectors.
149  QPointF* ptr = poly.data();
150  for ( unsigned int jdx = 0; jdx < nPoints; ++jdx, ++ptr )
151  {
152  memcpy( &x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
153  memcpy( &y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;
154 
155  *ptr = QPointF( x, y );
156  }
157 
158  if ( nPoints < 1 )
159  continue;
160 
161  //clip close to view extent, if needed
162  QRectF ptsRect = poly.boundingRect();
163  if ( !context.extent().contains( ptsRect ) ) QgsClipper::trimPolygon( poly, clipRect );
164 
165  //transform the QPolygonF to screen coordinates
166  if ( ct )
167  {
168  ct->transformPolygon( poly );
169  }
170 
171 
172  ptr = poly.data();
173  for ( int i = 0; i < poly.size(); ++i, ++ptr )
174  {
175  mtp.transformInPlace( ptr->rx(), ptr->ry() );
176  }
177 
178  if ( idx == 0 )
179  pts = poly;
180  else
181  holes.append( poly );
182  }
183 
184  return wkb;
185 }
186 
188 {
189  if ( symbol )
190  {
191  if ( symbol->type() == QgsSymbolV2::Marker )
192  {
193  QgsMarkerSymbolV2* ms = static_cast<QgsMarkerSymbolV2*>( symbol );
194  if ( ms )
195  {
196  ms->setScaleMethod(( QgsSymbolV2::ScaleMethod )scaleMethod );
197  }
198  }
199  }
200 }
201 
202 
204  : mType( type ), mUsingSymbolLevels( false ),
205  mCurrentVertexMarkerType( QgsVectorLayer::Cross ),
206  mCurrentVertexMarkerSize( 3 )
207 {
208 }
209 
211 {
212  return new QgsSingleSymbolRendererV2( QgsSymbolV2::defaultSymbol( geomType ) );
213 }
214 
215 
216 bool QgsFeatureRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
217 {
218  QgsSymbolV2* symbol = symbolForFeature( feature );
219  if ( symbol == NULL )
220  return false;
221 
222  renderFeatureWithSymbol( feature, symbol, context, layer, selected, drawVertexMarker );
223  return true;
224 }
225 
226 void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymbolV2* symbol, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
227 {
228  QgsSymbolV2::SymbolType symbolType = symbol->type();
229 
230  QgsGeometry* geom = feature.geometry();
231  switch ( geom->wkbType() )
232  {
233  case QGis::WKBPoint:
234  case QGis::WKBPoint25D:
235  {
236  if ( symbolType != QgsSymbolV2::Marker )
237  {
238  QgsDebugMsg( "point can be drawn only with marker symbol!" );
239  break;
240  }
241  QPointF pt;
242  _getPoint( pt, context, geom->asWkb() );
243  (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
244 
245  //if ( drawVertexMarker )
246  // renderVertexMarker( pt, context );
247  }
248  break;
249 
250  case QGis::WKBLineString:
252  {
253  if ( symbolType != QgsSymbolV2::Line )
254  {
255  QgsDebugMsg( "linestring can be drawn only with line symbol!" );
256  break;
257  }
258  QPolygonF pts;
259  _getLineString( pts, context, geom->asWkb() );
260  (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );
261 
262  if ( drawVertexMarker )
263  renderVertexMarkerPolyline( pts, context );
264  }
265  break;
266 
267  case QGis::WKBPolygon:
268  case QGis::WKBPolygon25D:
269  {
270  if ( symbolType != QgsSymbolV2::Fill )
271  {
272  QgsDebugMsg( "polygon can be drawn only with fill symbol!" );
273  break;
274  }
275  QPolygonF pts;
276  QList<QPolygonF> holes;
277  _getPolygon( pts, holes, context, geom->asWkb() );
278  (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
279 
280  if ( drawVertexMarker )
281  renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
282  }
283  break;
284 
285  case QGis::WKBMultiPoint:
287  {
288  if ( symbolType != QgsSymbolV2::Marker )
289  {
290  QgsDebugMsg( "multi-point can be drawn only with marker symbol!" );
291  break;
292  }
293 
294  const unsigned char* wkb = geom->asWkb();
295  unsigned int num = *(( int* )( wkb + 5 ) );
296  const unsigned char* ptr = wkb + 9;
297  QPointF pt;
298 
299  for ( unsigned int i = 0; i < num; ++i )
300  {
301  ptr = _getPoint( pt, context, ptr );
302  (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
303 
304  //if ( drawVertexMarker )
305  // renderVertexMarker( pt, context );
306  }
307  }
308  break;
309 
312  {
313  if ( symbolType != QgsSymbolV2::Line )
314  {
315  QgsDebugMsg( "multi-linestring can be drawn only with line symbol!" );
316  break;
317  }
318 
319  const unsigned char* wkb = geom->asWkb();
320  unsigned int num = *(( int* )( wkb + 5 ) );
321  const unsigned char* ptr = wkb + 9;
322  QPolygonF pts;
323 
324  for ( unsigned int i = 0; i < num; ++i )
325  {
326  ptr = _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  const unsigned char* wkb = geom->asWkb();
345  unsigned int num = *(( int* )( wkb + 5 ) );
346  const unsigned char* ptr = wkb + 9;
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 
511 QgsLegendSymbolList QgsFeatureRendererV2::legendSymbolItems( double scaleDenominator, QString rule )
512 {
513  Q_UNUSED( scaleDenominator );
514  Q_UNUSED( rule );
515  return QgsLegendSymbolList();
516 }
517 
519 {
522 }
523 
525 {
526  QgsVectorLayer::drawVertexMarker( pt.x(), pt.y(), *context.painter(),
529 }
530 
532 {
533  foreach ( QPointF pt, pts )
534  renderVertexMarker( pt, context );
535 }
536 
537 void QgsFeatureRendererV2::renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context )
538 {
539  foreach ( QPointF pt, pts )
540  renderVertexMarker( pt, context );
541 
542  if ( rings )
543  {
544  foreach ( QPolygonF ring, *rings )
545  {
546  foreach ( QPointF pt, ring )
547  renderVertexMarker( pt, context );
548  }
549  }
550 }
551 
553 {
554  QgsSymbolV2List lst;
555  QgsSymbolV2* s = symbolForFeature( feat );
556  if ( s ) lst.append( s );
557  return lst;
558 }