QGIS API Documentation  2.0.1-Dufour
 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  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  wkb = QgsClipper::clippedLineWKB( wkb - ( 2 * sizeof( unsigned int ) + 1 ), 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  x = *(( double * ) wkb );
90  wkb += sizeof( double );
91  y = *(( double * ) wkb );
92  wkb += sizeof( double );
93 
94  if ( hasZValue ) // ignore Z value
95  wkb += sizeof( double );
96 
97  *ptr = QPointF( x, y );
98  }
99  }
100 
101  //transform the QPolygonF to screen coordinates
102  if ( ct )
103  {
104  ct->transformPolygon( pts );
105  }
106 
107  QPointF* ptr = pts.data();
108  for ( int i = 0; i < pts.size(); ++i, ++ptr )
109  {
110  mtp.transformInPlace( ptr->rx(), ptr->ry() );
111  }
112 
113 
114  return wkb;
115 }
116 
117 const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb )
118 {
119  wkb++; // jump over endian info
120  unsigned int wkbType = *(( int* ) wkb );
121  wkb += sizeof( unsigned int ); // jump over wkb type
122  unsigned int numRings = *(( int* ) wkb );
123  wkb += sizeof( unsigned int );
124 
125  if ( numRings == 0 ) // sanity check for zero rings in polygon
126  return wkb;
127 
128  bool hasZValue = ( wkbType == QGis::WKBPolygon25D );
129  double x, y;
130  holes.clear();
131 
132  const QgsCoordinateTransform* ct = context.coordinateTransform();
133  const QgsMapToPixel& mtp = context.mapToPixel();
134  const QgsRectangle& e = context.extent();
135  double cw = e.width() / 10; double ch = e.height() / 10;
136  QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
137 
138  for ( unsigned int idx = 0; idx < numRings; idx++ )
139  {
140  unsigned int nPoints = *(( int* )wkb );
141  wkb += sizeof( unsigned int );
142 
143  QPolygonF poly( nPoints );
144 
145  // Extract the points from the WKB and store in a pair of vectors.
146  QPointF* ptr = poly.data();
147  for ( unsigned int jdx = 0; jdx < nPoints; ++jdx, ++ptr )
148  {
149  x = *(( double * ) wkb ); wkb += sizeof( double );
150  y = *(( double * ) wkb ); wkb += sizeof( double );
151 
152  *ptr = QPointF( x, y );
153 
154  if ( hasZValue )
155  wkb += sizeof( double );
156  }
157 
158  if ( nPoints < 1 )
159  continue;
160 
161  //clip close to view extent
162  QgsClipper::trimPolygon( poly, clipRect );
163 
164  //transform the QPolygonF to screen coordinates
165  if ( ct )
166  {
167  ct->transformPolygon( poly );
168  }
169 
170 
171  ptr = poly.data();
172  for ( int i = 0; i < poly.size(); ++i, ++ptr )
173  {
174  mtp.transformInPlace( ptr->rx(), ptr->ry() );
175  }
176 
177  if ( idx == 0 )
178  pts = poly;
179  else
180  holes.append( poly );
181  }
182 
183  return wkb;
184 }
185 
187 {
188  if ( symbol )
189  {
190  if ( symbol->type() == QgsSymbolV2::Marker )
191  {
192  QgsMarkerSymbolV2* ms = static_cast<QgsMarkerSymbolV2*>( symbol );
193  if ( ms )
194  {
195  ms->setScaleMethod(( QgsSymbolV2::ScaleMethod )scaleMethod );
196  }
197  }
198  }
199 }
200 
201 
203  : mType( type ), mUsingSymbolLevels( false ),
204  mCurrentVertexMarkerType( QgsVectorLayer::Cross ),
205  mCurrentVertexMarkerSize( 3 )
206 {
207 }
208 
210 {
211  return new QgsSingleSymbolRendererV2( QgsSymbolV2::defaultSymbol( geomType ) );
212 }
213 
214 
215 bool QgsFeatureRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
216 {
217  QgsSymbolV2* symbol = symbolForFeature( feature );
218  if ( symbol == NULL )
219  return false;
220 
221  renderFeatureWithSymbol( feature, symbol, context, layer, selected, drawVertexMarker );
222  return true;
223 }
224 
225 void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymbolV2* symbol, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
226 {
227  QgsSymbolV2::SymbolType symbolType = symbol->type();
228 
229  QgsGeometry* geom = feature.geometry();
230  switch ( geom->wkbType() )
231  {
232  case QGis::WKBPoint:
233  case QGis::WKBPoint25D:
234  {
235  if ( symbolType != QgsSymbolV2::Marker )
236  {
237  QgsDebugMsg( "point can be drawn only with marker symbol!" );
238  break;
239  }
240  QPointF pt;
241  _getPoint( pt, context, geom->asWkb() );
242  (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
243 
244  //if ( drawVertexMarker )
245  // renderVertexMarker( pt, context );
246  }
247  break;
248 
249  case QGis::WKBLineString:
251  {
252  if ( symbolType != QgsSymbolV2::Line )
253  {
254  QgsDebugMsg( "linestring can be drawn only with line symbol!" );
255  break;
256  }
257  QPolygonF pts;
258  _getLineString( pts, context, geom->asWkb() );
259  (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );
260 
261  if ( drawVertexMarker )
262  renderVertexMarkerPolyline( pts, context );
263  }
264  break;
265 
266  case QGis::WKBPolygon:
267  case QGis::WKBPolygon25D:
268  {
269  if ( symbolType != QgsSymbolV2::Fill )
270  {
271  QgsDebugMsg( "polygon can be drawn only with fill symbol!" );
272  break;
273  }
274  QPolygonF pts;
275  QList<QPolygonF> holes;
276  _getPolygon( pts, holes, context, geom->asWkb() );
277  (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
278 
279  if ( drawVertexMarker )
280  renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
281  }
282  break;
283 
284  case QGis::WKBMultiPoint:
286  {
287  if ( symbolType != QgsSymbolV2::Marker )
288  {
289  QgsDebugMsg( "multi-point can be drawn only with marker symbol!" );
290  break;
291  }
292 
293  const unsigned char* wkb = geom->asWkb();
294  unsigned int num = *(( int* )( wkb + 5 ) );
295  const unsigned char* ptr = wkb + 9;
296  QPointF pt;
297 
298  for ( unsigned int i = 0; i < num; ++i )
299  {
300  ptr = _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  const unsigned char* wkb = geom->asWkb();
319  unsigned int num = *(( int* )( wkb + 5 ) );
320  const unsigned char* ptr = wkb + 9;
321  QPolygonF pts;
322 
323  for ( unsigned int i = 0; i < num; ++i )
324  {
325  ptr = _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  const unsigned char* wkb = geom->asWkb();
344  unsigned int num = *(( int* )( wkb + 5 ) );
345  const unsigned char* ptr = wkb + 9;
346  QPolygonF pts;
347  QList<QPolygonF> holes;
348 
349  for ( unsigned int i = 0; i < num; ++i )
350  {
351  ptr = _getPolygon( pts, holes, context, ptr );
352  (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
353 
354  if ( drawVertexMarker )
355  renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
356  }
357  }
358  break;
359 
360  default:
361  QgsDebugMsg( QString( "feature %1: unsupported wkb type 0x%2 for rendering" ).arg( feature.id() ).arg( geom->wkbType(), 0, 16 ) );
362  }
363 }
364 
366 {
367  return "UNKNOWN RENDERER\n";
368 }
369 
370 
372 {
373  // <renderer-v2 type=""> ... </renderer-v2>
374 
375  if ( element.isNull() )
376  return NULL;
377 
378  // load renderer
379  QString rendererType = element.attribute( "type" );
380 
382  if ( m == NULL )
383  return NULL;
384 
385  QgsFeatureRendererV2* r = m->createRenderer( element );
386  if ( r )
387  {
388  r->setUsingSymbolLevels( element.attribute( "symbollevels", "0" ).toInt() );
389  }
390  return r;
391 }
392 
393 QDomElement QgsFeatureRendererV2::save( QDomDocument& doc )
394 {
395  // create empty renderer element
396  return doc.createElement( RENDERER_TAG_NAME );
397 }
398 
399 QgsFeatureRendererV2* QgsFeatureRendererV2::loadSld( const QDomNode &node, QGis::GeometryType geomType, QString &errorMessage )
400 {
401  QDomElement element = node.toElement();
402  if ( element.isNull() )
403  return NULL;
404 
405  // get the UserStyle element
406  QDomElement userStyleElem = element.firstChildElement( "UserStyle" );
407  if ( userStyleElem.isNull() )
408  {
409  // UserStyle element not found, nothing will be rendered
410  errorMessage = "Info: UserStyle element not found.";
411  return NULL;
412  }
413 
414  // get the FeatureTypeStyle element
415  QDomElement featTypeStyleElem = userStyleElem.firstChildElement( "FeatureTypeStyle" );
416  if ( featTypeStyleElem.isNull() )
417  {
418  errorMessage = "Info: FeatureTypeStyle element not found.";
419  return NULL;
420  }
421 
422  // use the RuleRenderer when more rules are present or the rule
423  // has filters or min/max scale denominators set,
424  // otherwise use the SingleSymbol renderer
425  bool needRuleRenderer = false;
426  int ruleCount = 0;
427 
428  QDomElement ruleElem = featTypeStyleElem.firstChildElement( "Rule" );
429  while ( !ruleElem.isNull() )
430  {
431  ruleCount++;
432 
433  // more rules present, use the RuleRenderer
434  if ( ruleCount > 1 )
435  {
436  QgsDebugMsg( "more Rule elements found: need a RuleRenderer" );
437  needRuleRenderer = true;
438  break;
439  }
440 
441  QDomElement ruleChildElem = ruleElem.firstChildElement();
442  while ( !ruleChildElem.isNull() )
443  {
444  // rule has filter or min/max scale denominator, use the RuleRenderer
445  if ( ruleChildElem.localName() == "Filter" ||
446  ruleChildElem.localName() == "MinScaleDenominator" ||
447  ruleChildElem.localName() == "MaxScaleDenominator" )
448  {
449  QgsDebugMsg( "Filter or Min/MaxScaleDenominator element found: need a RuleRenderer" );
450  needRuleRenderer = true;
451  break;
452  }
453 
454  ruleChildElem = ruleChildElem.nextSiblingElement();
455  }
456 
457  if ( needRuleRenderer )
458  {
459  break;
460  }
461 
462  ruleElem = ruleElem.nextSiblingElement( "Rule" );
463  }
464 
465  QString rendererType;
466  if ( needRuleRenderer )
467  {
468  rendererType = "RuleRenderer";
469  }
470  else
471  {
472  rendererType = "singleSymbol";
473  }
474  QgsDebugMsg( QString( "Instantiating a '%1' renderer..." ).arg( rendererType ) );
475 
476  // create the renderer and return it
478  if ( m == NULL )
479  {
480  errorMessage = QString( "Error: Unable to get metadata for '%1' renderer." ).arg( rendererType );
481  return NULL;
482  }
483 
484  QgsFeatureRendererV2* r = m->createRendererFromSld( featTypeStyleElem, geomType );
485  return r;
486 }
487 
488 QDomElement QgsFeatureRendererV2::writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const
489 {
490  QDomElement userStyleElem = doc.createElement( "UserStyle" );
491 
492  QDomElement nameElem = doc.createElement( "se:Name" );
493  nameElem.appendChild( doc.createTextNode( layer.name() ) );
494  userStyleElem.appendChild( nameElem );
495 
496  QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" );
497  toSld( doc, featureTypeStyleElem );
498  userStyleElem.appendChild( featureTypeStyleElem );
499 
500  return userStyleElem;
501 }
502 
504 {
505  Q_UNUSED( iconSize );
506  // empty list by default
507  return QgsLegendSymbologyList();
508 }
509 
511 {
512  return QgsLegendSymbolList();
513 }
514 
516 {
519 }
520 
522 {
523  QgsVectorLayer::drawVertexMarker( pt.x(), pt.y(), *context.painter(),
526 }
527 
529 {
530  foreach ( QPointF pt, pts )
531  renderVertexMarker( pt, context );
532 }
533 
534 void QgsFeatureRendererV2::renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context )
535 {
536  foreach ( QPointF pt, pts )
537  renderVertexMarker( pt, context );
538 
539  if ( rings )
540  {
541  foreach ( QPolygonF ring, *rings )
542  {
543  foreach ( QPointF pt, ring )
544  renderVertexMarker( pt, context );
545  }
546  }
547 }
548 
550 {
551  QgsSymbolV2List lst;
552  QgsSymbolV2* s = symbolForFeature( feat );
553  if ( s ) lst.append( s );
554  return lst;
555 }