QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgssymbolv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssymbolv2.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 "qgssymbolv2.h"
17 #include "qgssymbollayerv2.h"
18 
19 #include "qgslinesymbollayerv2.h"
20 #include "qgsmarkersymbollayerv2.h"
21 #include "qgsfillsymbollayerv2.h"
22 
23 #include "qgslogger.h"
24 #include "qgsrendercontext.h" // for bigSymbolPreview
25 
26 #include "qgsproject.h"
27 #include "qgsstylev2.h"
28 
29 #include <QColor>
30 #include <QImage>
31 #include <QPainter>
32 #include <QSize>
33 
34 #include <cmath>
35 
37  : mType( type ), mLayers( layers ), mAlpha( 1.0 ), mRenderHints( 0 )
38 {
39 
40  // check they're all correct symbol layers
41  for ( int i = 0; i < mLayers.count(); i++ )
42  {
43  if ( mLayers[i] == NULL )
44  {
45  mLayers.removeAt( i-- );
46  }
47  else if ( !isSymbolLayerCompatible( mLayers[i]->type() ) )
48  {
49  delete mLayers[i];
50  mLayers.removeAt( i-- );
51  }
52  }
53 
54 }
55 
57 {
58  // delete all symbol layers (we own them, so it's okay)
59  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
60  delete *it;
61 }
62 
64 {
66 
67  QgsSymbolLayerV2List::const_iterator it = mLayers.constBegin();
68  for ( ; it != mLayers.constEnd(); ++it )
69  {
70  if ( it == mLayers.constBegin() )
71  {
72  unit = ( *it )->outputUnit();
73  }
74  else
75  {
76  if (( *it )->outputUnit() != unit )
77  {
78  return QgsSymbolV2::Mixed;
79  }
80  }
81  }
82 
83  return unit;
84 }
85 
87 {
88  QgsSymbolLayerV2List::iterator it = mLayers.begin();
89  for ( ; it != mLayers.end(); ++it )
90  {
91  ( *it )->setOutputUnit( u );
92  }
93 }
94 
96 {
97  QgsSymbolV2* s = 0;
98 
99  // override global default if project has a default for this type
100  QString defaultSymbol;
101  switch ( geomType )
102  {
103  case QGis::Point :
104  defaultSymbol = QgsProject::instance()->readEntry( "DefaultStyles", "/Marker", "" );
105  break;
106  case QGis::Line :
107  defaultSymbol = QgsProject::instance()->readEntry( "DefaultStyles", "/Line", "" );
108  break;
109  case QGis::Polygon :
110  defaultSymbol = QgsProject::instance()->readEntry( "DefaultStyles", "/Fill", "" );
111  break;
112  default: defaultSymbol = ""; break;
113  }
114  if ( defaultSymbol != "" )
115  s = QgsStyleV2::defaultStyle()->symbol( defaultSymbol );
116 
117  // if no default found for this type, get global default (as previously)
118  if ( ! s )
119  {
120  switch ( geomType )
121  {
122  case QGis::Point: s = new QgsMarkerSymbolV2(); break;
123  case QGis::Line: s = new QgsLineSymbolV2(); break;
124  case QGis::Polygon: s = new QgsFillSymbolV2(); break;
125  default: QgsDebugMsg( "unknown layer's geometry type" ); return NULL;
126  }
127  }
128 
129  // set alpha transparency
130  s->setAlpha( QgsProject::instance()->readDoubleEntry( "DefaultStyles", "/AlphaInt", 255 ) / 255.0 );
131 
132  // set random color, it project prefs allow
133  if ( defaultSymbol == "" ||
134  QgsProject::instance()->readBoolEntry( "DefaultStyles", "/RandomColors", true ) )
135  {
136  s->setColor( QColor::fromHsv( rand() % 360, 64 + rand() % 192, 128 + rand() % 128 ) );
137  }
138 
139  return s;
140 }
141 
143 {
144  if ( layer < 0 || layer >= mLayers.count() )
145  return NULL;
146 
147  return mLayers[layer];
148 }
149 
150 
152 {
153  // fill symbol can contain also line symbol layers for drawing of outlines
154  if ( mType == Fill && t == Line )
155  return true;
156 
157  return mType == t;
158 }
159 
160 
162 {
163  if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
164  return false;
165  if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) )
166  return false;
167 
168  mLayers.insert( index, layer );
169  return true;
170 }
171 
172 
174 {
175  if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) )
176  return false;
177 
178  mLayers.append( layer );
179  return true;
180 }
181 
182 
184 {
185  if ( index < 0 || index >= mLayers.count() )
186  return false;
187 
188  delete mLayers[index];
189  mLayers.removeAt( index );
190  return true;
191 }
192 
193 
195 {
196  if ( index < 0 || index >= mLayers.count() )
197  return NULL;
198 
199  return mLayers.takeAt( index );
200 }
201 
202 
204 {
205  if ( index < 0 || index >= mLayers.count() )
206  return false;
207  if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) )
208  return false;
209 
210  delete mLayers[index]; // first delete the original layer
211  mLayers[index] = layer; // set new layer
212  return true;
213 }
214 
215 
217 {
218  QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints );
219  symbolContext.setLayer( layer );
220  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
221  ( *it )->startRender( symbolContext );
222 }
223 
225 {
226  QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints );
227  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
228  ( *it )->stopRender( symbolContext );
229 }
230 
231 void QgsSymbolV2::setColor( const QColor& color )
232 {
233  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
234  {
235  if ( !( *it )->isLocked() )
236  ( *it )->setColor( color );
237  }
238 }
239 
240 QColor QgsSymbolV2::color() const
241 {
242  for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
243  {
244  // return color of the first unlocked layer
245  if ( !( *it )->isLocked() )
246  return ( *it )->color();
247  }
248  return QColor( 0, 0, 0 );
249 }
250 
251 void QgsSymbolV2::drawPreviewIcon( QPainter* painter, QSize size )
252 {
254  QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints );
255  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
256  {
257  if ( mType == Fill && ( *it )->type() == Line )
258  {
259  // line symbol layer would normally draw just a line
260  // so we override this case to force it to draw a polygon outline
262 
263  // from QgsFillSymbolLayerV2::drawPreviewIcon()
264  QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width() - 1, size.height() - 1 ) );
265  lsl->startRender( symbolContext );
266  lsl->renderPolygonOutline( poly, NULL, symbolContext );
267  lsl->stopRender( symbolContext );
268  }
269  else
270  ( *it )->drawPreviewIcon( symbolContext, size );
271  }
272 }
273 
274 
276 {
277  QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
278  preview.fill( 0 );
279 
280  QPainter p( &preview );
281  p.setRenderHint( QPainter::Antialiasing );
282  p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialising
283 
284  if ( mType == QgsSymbolV2::Marker )
285  {
286  p.setPen( QPen( Qt::gray ) );
287  p.drawLine( 0, 50, 100, 50 );
288  p.drawLine( 50, 0, 50, 100 );
289  }
290 
292  startRender( context );
293 
294  if ( mType == QgsSymbolV2::Line )
295  {
296  QPolygonF poly;
297  poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
298  static_cast<QgsLineSymbolV2*>( this )->renderPolyline( poly, 0, context );
299  }
300  else if ( mType == QgsSymbolV2::Fill )
301  {
302  QPolygonF polygon;
303  polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
304  static_cast<QgsFillSymbolV2*>( this )->renderPolygon( polygon, NULL, 0, context );
305  }
306  else // marker
307  {
308  static_cast<QgsMarkerSymbolV2*>( this )->renderPoint( QPointF( 50, 50 ), 0, context );
309  }
310 
311  stopRender( context );
312  return preview;
313 }
314 
315 
316 QString QgsSymbolV2::dump() const
317 {
318  QString t;
319  switch ( type() )
320  {
321  case QgsSymbolV2::Marker: t = "MARKER"; break;
322  case QgsSymbolV2::Line: t = "LINE"; break;
323  case QgsSymbolV2::Fill: t = "FILL"; break;
324  default: Q_ASSERT( 0 && "unknown symbol type" );
325  }
326  QString s = QString( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerV2Utils::encodeColor( color() ) );
327 
328  for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
329  {
330  // TODO:
331  }
332  return s;
333 }
334 
335 void QgsSymbolV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
336 {
337  props[ "alpha" ] = QString::number( alpha() );
338  double scaleFactor = 1.0;
339  props[ "uom" ] = QgsSymbolLayerV2Utils::encodeSldUom( outputUnit(), &scaleFactor );
340  props[ "uomScale" ] = scaleFactor != 1 ? QString::number( scaleFactor ) : "";
341 
342  for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
343  {
344  ( *it )->toSld( doc, element, props );
345  }
346 }
347 
349 {
351  for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
352  {
353  QgsSymbolLayerV2* layer = ( *it )->clone();
354  layer->setLocked(( *it )->isLocked() );
355  layer->setRenderingPass(( *it )->renderingPass() );
356  lst.append( layer );
357  }
358  return lst;
359 }
360 
361 QSet<QString> QgsSymbolV2::usedAttributes() const
362 {
363  QSet<QString> attributes;
364  QgsSymbolLayerV2List::const_iterator sIt = mLayers.constBegin();
365  for ( ; sIt != mLayers.constEnd(); ++sIt )
366  {
367  if ( *sIt )
368  {
369  attributes.unite(( *sIt )->usedAttributes() );
370  }
371  }
372  return attributes;
373 }
374 
376 
377 
378 QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u, qreal alpha, bool selected, int renderHints, const QgsFeature* f )
379  : mRenderContext( c ), mOutputUnit( u ), mAlpha( alpha ), mSelected( selected ), mRenderHints( renderHints ), mFeature( f ), mLayer( 0 )
380 {
381 
382 }
383 
385 {
386 
387 }
388 
389 
390 double QgsSymbolV2RenderContext::outputLineWidth( double width ) const
391 {
393 }
394 
396 {
398 }
399 
401 {
402  // This is just a dummy implementation of assignment.
403  // sip 4.7 generates a piece of code that needs this function to exist.
404  // It's not generated automatically by the compiler because of
405  // mRenderContext member which is a reference (and thus can't be changed).
406  Q_ASSERT( false );
407  return *this;
408 }
409 
411 
413 {
415  if ( sl == NULL )
416  return NULL;
417 
418  QgsSymbolLayerV2List layers;
419  layers.append( sl );
420  return new QgsMarkerSymbolV2( layers );
421 }
422 
424 {
426  if ( sl == NULL )
427  return NULL;
428 
429  QgsSymbolLayerV2List layers;
430  layers.append( sl );
431  return new QgsLineSymbolV2( layers );
432 }
433 
435 {
437  if ( sl == NULL )
438  return NULL;
439 
440  QgsSymbolLayerV2List layers;
441  layers.append( sl );
442  return new QgsFillSymbolV2( layers );
443 }
444 
446 
447 
449  : QgsSymbolV2( Marker, layers )
450 {
451  if ( mLayers.count() == 0 )
452  mLayers.append( new QgsSimpleMarkerSymbolLayerV2() );
453 }
454 
455 void QgsMarkerSymbolV2::setAngle( double ang )
456 {
457  double origAngle = angle();
458  double angleDiff = ang - origAngle;
459  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
460  {
462  layer->setAngle( layer->angle() + angleDiff );
463  }
464 }
465 
467 {
468  QgsSymbolLayerV2List::const_iterator it = mLayers.begin();
469 
470  if ( it == mLayers.end() )
471  return 0;
472 
473  // return angle of the first symbol layer
474  const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
475  return layer->angle();
476 }
477 
479 {
480  double origSize = size();
481 
482  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
483  {
484  QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2*>( *it );
485  if ( layer->size() == origSize )
486  layer->setSize( s );
487  else
488  {
489  // proportionally scale size
490  if ( origSize != 0 )
491  layer->setSize( layer->size() * s / origSize );
492  }
493  }
494 }
495 
497 {
498  // return size of the largest symbol
499  double maxSize = 0;
500  for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
501  {
502  const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
503  double lsize = layer->size();
504  if ( lsize > maxSize )
505  maxSize = lsize;
506  }
507  return maxSize;
508 }
509 
510 
512 {
513  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
514  {
515  QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2*>( *it );
516  layer->setScaleMethod( scaleMethod );
517  }
518 }
519 
521 {
522  QgsSymbolLayerV2List::const_iterator it = mLayers.begin();
523 
524  if ( it == mLayers.end() )
525  return DEFAULT_SCALE_METHOD;
526 
527  // return scale method of the first symbol layer
528  const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
529  return layer->scaleMethod();
530 }
531 
532 void QgsMarkerSymbolV2::renderPoint( const QPointF& point, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
533 {
534  QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, selected, mRenderHints, f );
535  if ( layer != -1 )
536  {
537  if ( layer >= 0 && layer < mLayers.count() )
538  (( QgsMarkerSymbolLayerV2* ) mLayers[layer] )->renderPoint( point, symbolContext );
539  return;
540  }
541 
542  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
543  {
545  layer->renderPoint( point, symbolContext );
546  }
547 }
548 
550 {
551  QgsSymbolV2* cloneSymbol = new QgsMarkerSymbolV2( cloneLayers() );
552  cloneSymbol->setAlpha( mAlpha );
553  return cloneSymbol;
554 }
555 
556 
558 // LINE
559 
561  : QgsSymbolV2( Line, layers )
562 {
563  if ( mLayers.count() == 0 )
564  mLayers.append( new QgsSimpleLineSymbolLayerV2() );
565 }
566 
568 {
569  double origWidth = width();
570 
571  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
572  {
573  QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it;
574  if ( layer->width() == origWidth )
575  {
576  layer->setWidth( w );
577  }
578  else
579  {
580  // proportionally scale the width
581  if ( origWidth != 0 )
582  layer->setWidth( layer->width() * w / origWidth );
583  }
584  }
585 }
586 
588 {
589  double maxWidth = 0;
590  for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
591  {
592  const QgsLineSymbolLayerV2* layer = ( const QgsLineSymbolLayerV2* ) * it;
593  double width = layer->width();
594  if ( width > maxWidth )
595  maxWidth = width;
596  }
597  return maxWidth;
598 }
599 
600 void QgsLineSymbolV2::renderPolyline( const QPolygonF& points, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
601 {
602  QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, selected, mRenderHints, f );
603  if ( layer != -1 )
604  {
605  if ( layer >= 0 && layer < mLayers.count() )
606  (( QgsLineSymbolLayerV2* ) mLayers[layer] )->renderPolyline( points, symbolContext );
607  return;
608  }
609 
610  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
611  {
612  QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it;
613  layer->renderPolyline( points, symbolContext );
614  }
615 }
616 
617 
619 {
620  QgsSymbolV2* cloneSymbol = new QgsLineSymbolV2( cloneLayers() );
621  cloneSymbol->setAlpha( mAlpha );
622  return cloneSymbol;
623 }
624 
626 // FILL
627 
629  : QgsSymbolV2( Fill, layers )
630 {
631  if ( mLayers.count() == 0 )
632  mLayers.append( new QgsSimpleFillSymbolLayerV2() );
633 }
634 
635 void QgsFillSymbolV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
636 {
637  QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, selected, mRenderHints, f );
638  if ( layer != -1 )
639  {
640  if ( layer >= 0 && layer < mLayers.count() )
641  {
642  QgsSymbolV2::SymbolType layertype = mLayers.at( layer )->type();
643  if ( layertype == QgsSymbolV2::Fill )
644  (( QgsFillSymbolLayerV2* ) mLayers[layer] )->renderPolygon( points, rings, symbolContext );
645  else if ( layertype == QgsSymbolV2::Line )
646  (( QgsLineSymbolLayerV2* ) mLayers[layer] )->renderPolygonOutline( points, rings, symbolContext );
647  }
648  return;
649  }
650 
651  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
652  {
653  QgsSymbolV2::SymbolType layertype = ( *it )->type();
654  if ( layertype == QgsSymbolV2::Fill )
655  {
656  QgsFillSymbolLayerV2* layer = ( QgsFillSymbolLayerV2* ) * it;
657  layer->renderPolygon( points, rings, symbolContext );
658  }
659  else if ( layertype == QgsSymbolV2::Line )
660  {
661  QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it;
662  layer->renderPolygonOutline( points, rings, symbolContext );
663  }
664  }
665 }
666 
667 
669 {
670  QgsSymbolV2* cloneSymbol = new QgsFillSymbolV2( cloneLayers() );
671  cloneSymbol->setAlpha( mAlpha );
672  return cloneSymbol;
673 }
674 
676 {
677  for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
678  {
679  QgsFillSymbolLayerV2* layer = ( QgsFillSymbolLayerV2* ) * it;
680  layer->setAngle( angle );
681  }
682 }