QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgslabel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslabel.cpp - render vector labels
3  -------------------
4  begin : August 2004
5  copyright : (C) 2004 by Radim Blazek
6  email : blazek@itc.it
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include <cmath>
18 #include <limits>
19 
20 #include <QString>
21 #include <QFont>
22 #include <QFontMetrics>
23 
24 #include <QPainter>
25 #include <QDomNode>
26 #include <QDomElement>
27 
28 #include "qgis.h"
29 #include "qgsfeature.h"
30 #include "qgsgeometry.h"
31 #include "qgsfield.h"
32 #include "qgslogger.h"
33 #include "qgsrectangle.h"
34 #include "qgsmaptopixel.h"
35 #include "qgscoordinatetransform.h"
36 #include "qgsrendercontext.h"
37 
38 #include "qgslabelattributes.h"
39 #include "qgslabel.h"
40 
41 // use M_PI define PI 3.141592654
42 #ifdef WIN32
43 #undef M_PI
44 #define M_PI 4*atan(1.0)
45 #endif
46 
47 QgsLabel::QgsLabel( const QgsFields & fields )
48  : mMinScale( 0 ),
49  mMaxScale( 100000000 ),
50  mScaleBasedVisibility( false )
51 {
52  mFields = fields;
53  mLabelFieldIdx.resize( LabelFieldCount );
54  for ( int i = 0; i < LabelFieldCount; i++ )
55  {
56  mLabelFieldIdx[i] = -1;
57  }
58  mLabelAttributes = new QgsLabelAttributes( true );
59 }
60 
62 {
63  delete mLabelAttributes;
64 }
65 
66 QString QgsLabel::fieldValue( int attr, QgsFeature &feature )
67 {
68  int idx = mLabelFieldIdx[attr];
69  return idx < 0 ? QString() : feature.attribute( idx ).toString();
70 }
71 
73  QgsFeature &feature, bool selected,
74  QgsLabelAttributes *classAttributes )
75 {
76  Q_UNUSED( classAttributes );
77 
78  if ( mLabelAttributes->selectedOnly() && !selected )
79  return;
80 
81  QPen pen;
82  QFont font;
83  QString value;
84  QString text;
85 
86  /* Calc scale (not nice) */
87  QgsPoint point;
88  point = renderContext.mapToPixel().transform( 0, 0 );
89  double x1 = point.x();
90  point = renderContext.mapToPixel().transform( 1000, 0 );
91  double x2 = point.x();
92  double scale = ( x2 - x1 ) * 0.001;
93 
94  /* Text */
95  value = fieldValue( Text, feature );
96  if ( value.isEmpty() )
97  {
98  text = mLabelAttributes->text();
99  }
100  else
101  {
102  text = value;
103  }
104 
105  /* Font */
106  value = fieldValue( Family, feature );
107  if ( value.isEmpty() )
108  {
109  font.setFamily( mLabelAttributes->family() );
110  }
111  else
112  {
113  font.setFamily( value );
114  }
115 
116  double size;
117  value = fieldValue( Size, feature );
118  if ( value.isEmpty() )
119  {
120  size = mLabelAttributes->size();
121  }
122  else
123  {
124  size = value.toDouble();
125  }
126  int sizeType;
127  value = fieldValue( SizeType, feature );
128  if ( value.isEmpty() )
129  sizeType = mLabelAttributes->sizeType();
130  else
131  {
132  value = value.toLower();
133  if ( value.compare( "mapunits" ) == 0 )
134  sizeType = QgsLabelAttributes::MapUnits;
135  else
137  }
138  if ( sizeType == QgsLabelAttributes::MapUnits )
139  {
140  size *= scale;
141  }
142  else //point units
143  {
144  double sizeMM = size * 0.3527;
145  size = sizeMM * renderContext.scaleFactor();
146  }
147 
148  //Request font larger (multiplied by rasterScaleFactor) as a workaround for the Qt font bug
149  //and scale the painter down by rasterScaleFactor when drawing the label
150  size *= renderContext.rasterScaleFactor();
151 
152  if (( int )size <= 0 )
153  // skip too small labels
154  return;
155 
156  font.setPixelSize( size );
157 
158  value = fieldValue( Color, feature );
159  if ( value.isEmpty() )
160  {
161  pen.setColor( mLabelAttributes->color() );
162  }
163  else
164  {
165  pen.setColor( QColor( value ) );
166  }
167 
168  value = fieldValue( Bold, feature );
169  if ( value.isEmpty() )
170  {
171  font.setBold( mLabelAttributes->bold() );
172  }
173  else
174  {
175  font.setBold(( bool ) value.toInt() );
176  }
177 
178  value = fieldValue( Italic, feature );
179  if ( value.isEmpty() )
180  {
181  font.setItalic( mLabelAttributes->italic() );
182  }
183  else
184  {
185  font.setItalic(( bool ) value.toInt() );
186  }
187 
188  value = fieldValue( Underline, feature );
189  if ( value.isEmpty() )
190  {
191  font.setUnderline( mLabelAttributes->underline() );
192  }
193  else
194  {
195  font.setUnderline(( bool ) value.toInt() );
196  }
197 
198  value = fieldValue( StrikeOut, feature );
199  if ( value.isEmpty() )
200  {
201  font.setStrikeOut( mLabelAttributes->strikeOut() );
202  }
203  else
204  {
205  font.setStrikeOut(( bool ) value.toInt() );
206  }
207 
208  //
209  QgsPoint overridePoint;
210  bool useOverridePoint = false;
211  value = fieldValue( XCoordinate, feature );
212  if ( !value.isEmpty() )
213  {
214  overridePoint.setX( value.toDouble() );
215  useOverridePoint = true;
216  }
217  value = fieldValue( YCoordinate, feature );
218  if ( !value.isEmpty() )
219  {
220  overridePoint.setY( value.toDouble() );
221  useOverridePoint = true;
222  }
223 
224  /* Alignment */
225  int alignment;
226  QFontMetrics fm( font );
227  int width, height;
228 
229  if ( mLabelAttributes->multilineEnabled() )
230  {
231  QStringList texts = text.split( "\n" );
232 
233  width = 0;
234  for ( int i = 0; i < texts.size(); i++ )
235  {
236  int w = fm.width( texts[i] );
237  if ( w > width )
238  width = w;
239  }
240 
241  height = fm.height() * texts.size();
242  }
243  else
244  {
245  width = fm.width( text );
246  height = fm.height();
247  }
248 
249  int dx = 0;
250  int dy = 0;
251 
252  value = fieldValue( Alignment, feature );
253  if ( value.isEmpty() )
254  {
255  alignment = mLabelAttributes->alignment();
256  }
257  else
258  {
259  value = value.toLower();
260 
261  alignment = 0;
262 
263  if ( value.contains( "left" ) )
264  alignment |= Qt::AlignLeft;
265  else if ( value.contains( "right" ) )
266  alignment |= Qt::AlignRight;
267  else
268  alignment |= Qt::AlignHCenter;
269 
270  if ( value.contains( "bottom" ) )
271  alignment |= Qt::AlignBottom;
272  else if ( value.contains( "top" ) )
273  alignment |= Qt::AlignTop;
274  else
275  alignment |= Qt::AlignVCenter;
276  }
277 
278  if ( alignment & Qt::AlignLeft )
279  {
280  dx = 0;
281  }
282  else if ( alignment & Qt::AlignHCenter )
283  {
284  dx = -width / 2;
285  }
286  else if ( alignment & Qt::AlignRight )
287  {
288  dx = -width;
289  }
290 
291  if ( alignment & Qt::AlignBottom )
292  {
293  dy = 0;
294  }
295  else if ( alignment & Qt::AlignVCenter )
296  {
297  dy = height / 2;
298  }
299  else if ( alignment & Qt::AlignTop )
300  {
301  dy = height;
302  }
303 
304  // Offset
305  double xoffset, yoffset;
306  value = fieldValue( XOffset, feature );
307  if ( value.isEmpty() )
308  {
309  xoffset = mLabelAttributes->xOffset();
310  }
311  else
312  {
313  xoffset = value.toDouble();
314  }
315  value = fieldValue( YOffset, feature );
316  if ( value.isEmpty() )
317  {
318  yoffset = mLabelAttributes->yOffset();
319  }
320  else
321  {
322  yoffset = value.toDouble();
323  }
324 
325  // recalc offset to pixels
326  if ( mLabelAttributes->offsetType() == QgsLabelAttributes::MapUnits )
327  {
328  xoffset *= scale;
329  yoffset *= scale;
330  }
331  else
332  {
333  xoffset = xoffset * 0.3527 * renderContext.scaleFactor();
334  yoffset = yoffset * 0.3527 * renderContext.scaleFactor();
335  }
336 
337  // Angle
338  double ang;
339  value = fieldValue( Angle, feature );
340  if ( value.isEmpty() )
341  {
342  ang = mLabelAttributes->angle();
343  }
344  else
345  {
346  ang = value.toDouble();
347  }
348 
349 
350  // Work out a suitable position to put the label for the
351  // feature. For multi-geometries, put the same label on each
352  // part.
353  if ( useOverridePoint )
354  {
355  renderLabel( renderContext, overridePoint, text, font, pen, dx, dy,
356  xoffset, yoffset, ang, width, height, alignment );
357  }
358  else
359  {
360  std::vector<labelpoint> points;
361  labelPoint( points, feature );
362  for ( uint i = 0; i < points.size(); ++i )
363  {
364  renderLabel( renderContext, points[i].p, text, font, pen, dx, dy,
365  xoffset, yoffset, mLabelAttributes->angleIsAuto() ? points[i].angle : ang, width, height, alignment );
366  }
367  }
368 }
369 
370 void QgsLabel::renderLabel( QgsRenderContext &renderContext,
371  QgsPoint point,
372  QString text, QFont font, QPen pen,
373  int dx, int dy,
374  double xoffset, double yoffset,
375  double ang,
376  int width, int height, int alignment )
377 {
378  QPainter *painter = renderContext.painter();
379 
380  // Convert point to projected units
381  if ( renderContext.coordinateTransform() )
382  {
383  try
384  {
385  point = renderContext.coordinateTransform()->transform( point );
386  }
387  catch ( QgsCsException &cse )
388  {
389  Q_UNUSED( cse ); // unused otherwise
390  QgsDebugMsg( "Caught transform error. Skipping rendering this label" );
391  return;
392  }
393  }
394 
395  // and then to canvas units
396  renderContext.mapToPixel().transform( &point );
397  double x = point.x();
398  double y = point.y();
399 
400  double rad = ang * M_PI / 180;
401 
402  x = x + xoffset * cos( rad ) - yoffset * sin( rad );
403  y = y - xoffset * sin( rad ) - yoffset * cos( rad );
404 
405  painter->save();
406  painter->setFont( font );
407  painter->translate( x, y );
408  //correct oversampled font size back by scaling painter down
409  painter->scale( 1.0 / renderContext.rasterScaleFactor(), 1.0 / renderContext.rasterScaleFactor() );
410  painter->rotate( -ang );
411 
412  //
413  // Draw a buffer behind the text if one is desired
414  //
415  if ( mLabelAttributes->bufferSizeIsSet() && mLabelAttributes->bufferEnabled() )
416  {
417  double myBufferSize = mLabelAttributes->bufferSize() * 0.3527 * renderContext.scaleFactor() * renderContext.rasterScaleFactor();
418  QPen bufferPen;
419  if ( mLabelAttributes->bufferColorIsSet() )
420  {
421  bufferPen.setColor( mLabelAttributes->bufferColor() );
422  }
423  else //default to a white buffer
424  {
425  bufferPen.setColor( Qt::white );
426  }
427  painter->setPen( bufferPen );
428 
429  double bufferStepSize; //hack to distinguish pixel devices from logical devices
430  if (( renderContext.scaleFactor() - 1 ) > 1.5 )
431  {
432  bufferStepSize = 1;
433  }
434  else //draw more dense in case of logical devices
435  {
436  bufferStepSize = 1 / renderContext.rasterScaleFactor();
437  }
438 
439  for ( double i = dx - myBufferSize; i <= dx + myBufferSize; i += bufferStepSize )
440  {
441  for ( double j = dy - myBufferSize; j <= dy + myBufferSize; j += bufferStepSize )
442  {
443  if ( mLabelAttributes->multilineEnabled() )
444  painter->drawText( QRectF( i, j - height, width, height ), alignment, text );
445  else
446  painter->drawText( QPointF( i, j ), text );
447  }
448  }
449  }
450 
451  painter->setPen( pen );
452  if ( mLabelAttributes->multilineEnabled() )
453  painter->drawText( dx, dy - height, width, height, alignment, text );
454  else
455  painter->drawText( dx, dy, text );
456  painter->restore();
457 }
458 
460 {
461  for ( uint i = 0; i < LabelFieldCount; i++ )
462  {
463  if ( mLabelFieldIdx[i] == -1 )
464  continue;
465  bool found = false;
466  for ( QgsAttributeList::iterator it = fields.begin(); it != fields.end(); ++it )
467  {
468  if ( *it == mLabelFieldIdx[i] )
469  {
470  found = true;
471  break;
472  }
473  }
474  if ( !found )
475  {
476  fields.append( mLabelFieldIdx[i] );
477  }
478  }
479 }
480 
481 void QgsLabel::setFields( const QgsFields & fields )
482 {
483  mFields = fields;
484 }
485 
487 {
488  return mFields;
489 }
490 
491 void QgsLabel::setLabelField( int attr, int fieldIndex )
492 {
493  if ( attr >= LabelFieldCount )
494  return;
495 
496  mLabelFieldIdx[attr] = fieldIndex;
497 }
498 
499 QString QgsLabel::labelField( int attr ) const
500 {
501  if ( attr >= LabelFieldCount )
502  return QString();
503 
504  int fieldIndex = mLabelFieldIdx[attr];
505  if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
506  return QString();
507  return mFields[fieldIndex].name();
508 }
509 
511 {
512  return mLabelAttributes;
513 }
514 
515 void QgsLabel::labelPoint( std::vector<labelpoint>& points, QgsFeature & feature )
516 {
517  QgsGeometry *geometry = feature.geometry();
518  const unsigned char *geom = geometry->asWkb();
519  size_t geomlen = geometry->wkbSize();
520  QGis::WkbType wkbType = geometry->wkbType();
521  labelpoint point;
522 
523  switch ( wkbType )
524  {
525  case QGis::WKBPoint25D:
526  case QGis::WKBPoint:
528  case QGis::WKBLineString:
529  case QGis::WKBPolygon25D:
530  case QGis::WKBPolygon:
531  {
532  labelPoint( point, geom, geomlen );
533  points.push_back( point );
534  }
535  break;
536 
538  case QGis::WKBMultiPoint:
543  // Return a position for each individual in the multi-feature
544  {
545  Q_ASSERT( 1 + sizeof( wkbType ) + sizeof( int ) <= geomlen );
546  geom += 1 + sizeof( wkbType );
547  int nFeatures = *( unsigned int * )geom;
548  geom += sizeof( int );
549 
550  const unsigned char *feature = geom;
551  for ( int i = 0; i < nFeatures && feature; ++i )
552  {
553  feature = labelPoint( point, feature, geom + geomlen - feature );
554  points.push_back( point );
555  }
556  }
557  break;
558  default:
559  QgsDebugMsg( "Unknown geometry type of " + QString::number( wkbType ) );
560  }
561 }
562 
563 const unsigned char* QgsLabel::labelPoint( labelpoint& point, const unsigned char *geom, size_t geomlen )
564 {
565  // verify that local types match sizes as WKB spec
566  Q_ASSERT( sizeof( int ) == 4 );
567  Q_ASSERT( sizeof( QGis::WkbType ) == 4 );
568  Q_ASSERT( sizeof( double ) == 8 );
569 
570  if ( geom == NULL )
571  {
572  QgsDebugMsg( "empty wkb" );
573  return NULL;
574  }
575 
576  QGis::WkbType wkbType;
577 #ifndef QT_NO_DEBUG
578  const unsigned char *geomend = geom + geomlen;
579 #else
580  Q_UNUSED( geomlen );
581 #endif
582  Q_ASSERT( geom + 1 + sizeof( wkbType ) <= geomend );
583 
584  geom++; // skip endianness
585  memcpy( &wkbType, geom, sizeof( wkbType ) );
586  geom += sizeof( wkbType );
587 
588  int dims = 2;
589 
590  switch ( wkbType )
591  {
592  case QGis::WKBPoint25D:
593  case QGis::WKBPoint:
594  {
595  Q_ASSERT( geom + 2*sizeof( double ) <= geomend );
596  double *pts = ( double * )geom;
597  point.p.set( pts[0], pts[1] );
598  point.angle = 0.0;
599  geom += 2 * sizeof( double );
600  }
601  break;
602 
604  dims = 3;
605  //intentional fall-through
606  case QGis::WKBLineString: // Line center
607  {
608  Q_ASSERT( geom + sizeof( int ) <= geomend );
609  int nPoints = *( unsigned int * )geom;
610  geom += sizeof( int );
611 
612  Q_ASSERT( geom + nPoints*sizeof( double )*dims <= geomend );
613 
614  // get line center
615  double *pts = ( double * )geom;
616  double tl = 0.0;
617  for ( int i = 1; i < nPoints; i++ )
618  {
619  double dx = pts[dims*i] - pts[dims*( i-1 )];
620  double dy = pts[dims*i+1] - pts[dims*( i-1 )+1];
621  tl += sqrt( dx * dx + dy * dy );
622  }
623  tl /= 2.0;
624 
625  // find line center
626  double l = 0.0;
627  for ( int i = 1; i < nPoints; i++ )
628  {
629  double dx = pts[dims*i] - pts[dims*( i-1 )];
630  double dy = pts[dims*i+1] - pts[dims*( i-1 )+1];
631  double dl = sqrt( dx * dx + dy * dy );
632 
633  if ( l + dl > tl )
634  {
635  double k = ( tl - l ) / dl;
636 
637  point.p.set( pts[dims*( i-1 )] + k * dx,
638  pts[dims*( i-1 )+1] + k * dy );
639  point.angle = atan2( dy, dx ) * 180.0 * M_1_PI;
640  break;
641  }
642 
643  l += dl;
644  }
645 
646  geom += nPoints * sizeof( double ) * dims;
647  }
648  break;
649 
650  case QGis::WKBPolygon25D:
651  dims = 3;
652  //intentional fall-through
653  case QGis::WKBPolygon: // centroid of outer ring
654  {
655  Q_ASSERT( geom + sizeof( int ) <= geomend );
656  int nRings = *( unsigned int * )geom;
657  geom += sizeof( int );
658 
659  for ( int i = 0; i < nRings; ++i )
660  {
661  Q_ASSERT( geom + sizeof( int ) <= geomend );
662  int nPoints = *( unsigned int * )geom;
663  geom += sizeof( int );
664 
665  Q_ASSERT( geom + nPoints*sizeof( double )*dims <= geomend );
666 
667  if ( i == 0 )
668  {
669  double sx = 0.0, sy = 0.0;
670  double *pts = ( double* ) geom;
671  for ( int j = 0; j < nPoints - 1; j++ )
672  {
673  sx += pts[dims*j];
674  sy += pts[dims*j+1];
675  }
676  point.p.set( sx / ( nPoints - 1 ),
677  sy / ( nPoints - 1 ) );
678  point.angle = 0.0;
679  }
680 
681  geom += nPoints * sizeof( double ) * dims;
682  }
683  }
684  break;
685 
686  default:
687  // To get here is a bug because our caller should be filtering
688  // on wkb type.
689  QgsDebugMsg( "unsupported wkb type" );
690  return NULL;
691  }
692 
693  return geom;
694 }
695 
696 bool QgsLabel::readLabelField( QDomElement &el, int attr, QString prefix = "field" )
697 {
698  QString name = prefix + "name";
699 
700  if ( el.hasAttribute( name ) )
701  {
702  name = el.attribute( name );
703 
704  int idx = 0;
705  for ( ; idx < mFields.count(); ++idx )
706  {
707  if ( mFields[idx].name() == name )
708  {
709  break;
710  }
711  }
712 
713  if ( idx != mFields.count() )
714  {
715  mLabelFieldIdx[attr] = idx;
716  return true;
717  }
718  }
719  else if ( el.hasAttribute( prefix ) )
720  {
721  QString value = el.attribute( prefix );
722  mLabelFieldIdx[attr] = value.isEmpty() ? -1 : value.toInt();
723  return true;
724  }
725 
726  mLabelFieldIdx[attr] = -1;
727  return false;
728 }
729 
730 
731 void QgsLabel::readXML( const QDomNode& node )
732 {
733  QgsDebugMsg( " called for layer label properties, got node " + node.nodeName() );
734 
735  QDomNode scratchNode; // Dom node re-used to get current QgsLabel attribute
736  QDomElement el;
737 
738  int red, green, blue;
739  int type;
740 
741  /* Text */
742  scratchNode = node.namedItem( "label" );
743 
744  if ( scratchNode.isNull() )
745  {
746  QgsDebugMsg( "couldn't find QgsLabel ``label'' attribute" );
747  }
748  else
749  {
750  el = scratchNode.toElement();
751  mLabelAttributes->setText( el.attribute( "text", "" ) );
752  readLabelField( el, Text );
753  }
754 
755  /* Family */
756  scratchNode = node.namedItem( "family" );
757 
758  if ( scratchNode.isNull() )
759  {
760  QgsDebugMsg( "couldn't find QgsLabel ``family'' attribute" );
761  }
762  else
763  {
764  el = scratchNode.toElement();
765  mLabelAttributes->setFamily( el.attribute( "name", "" ) );
766  readLabelField( el, Family );
767  }
768 
769  /* Size */
770  scratchNode = node.namedItem( "size" );
771 
772  if ( scratchNode.isNull() )
773  {
774  QgsDebugMsg( "couldn't find QgsLabel ``size'' attribute" );
775  }
776  else
777  {
778  el = scratchNode.toElement();
779  if ( !el.hasAttribute( "unitfield" ) && !el.hasAttribute( "unitfieldname" ) )
780  {
781  type = QgsLabelAttributes::unitsCode( el.attribute( "units", "" ) );
782  mLabelAttributes->setSize( el.attribute( "value", "0.0" ).toDouble(), type );
783  }
784  else
785  {
786  readLabelField( el, SizeType, "unitfield" );
787  }
788  readLabelField( el, Size );
789  }
790 
791  /* Bold */
792  scratchNode = node.namedItem( "bold" );
793 
794  if ( scratchNode.isNull() )
795  {
796  QgsDebugMsg( "couldn't find QgsLabel ``bold'' attribute" );
797  }
798  else
799  {
800  el = scratchNode.toElement();
801  mLabelAttributes->setBold(( bool )el.attribute( "on", "0" ).toInt() );
802  readLabelField( el, Bold );
803  }
804 
805  /* Italic */
806  scratchNode = node.namedItem( "italic" );
807 
808  if ( scratchNode.isNull() )
809  {
810  QgsDebugMsg( "couldn't find QgsLabel ``italic'' attribute" );
811  }
812  else
813  {
814  el = scratchNode.toElement();
815  mLabelAttributes->setItalic(( bool )el.attribute( "on", "0" ).toInt() );
816  readLabelField( el, Italic );
817  }
818 
819  /* Underline */
820  scratchNode = node.namedItem( "underline" );
821 
822  if ( scratchNode.isNull() )
823  {
824  QgsDebugMsg( "couldn't find QgsLabel ``underline'' attribute" );
825  }
826  else
827  {
828  el = scratchNode.toElement();
829  mLabelAttributes->setUnderline(( bool )el.attribute( "on", "0" ).toInt() );
830  readLabelField( el, Underline );
831  }
832 
833  /* Strikeout */
834  scratchNode = node.namedItem( "strikeout" );
835 
836  if ( scratchNode.isNull() )
837  {
838  QgsDebugMsg( "couldn't find QgsLabel ``strikeout'' attribute" );
839  }
840  else
841  {
842  el = scratchNode.toElement();
843  mLabelAttributes->setStrikeOut(( bool )el.attribute( "on", "0" ).toInt() );
844  readLabelField( el, StrikeOut );
845  }
846 
847  /* Color */
848  scratchNode = node.namedItem( "color" );
849 
850  if ( scratchNode.isNull() )
851  {
852  QgsDebugMsg( "couldn't find QgsLabel ``color'' attribute" );
853  }
854  else
855  {
856  el = scratchNode.toElement();
857 
858  red = el.attribute( "red", "0" ).toInt();
859  green = el.attribute( "green", "0" ).toInt();
860  blue = el.attribute( "blue", "0" ).toInt();
861 
862  mLabelAttributes->setColor( QColor( red, green, blue ) );
863 
864  readLabelField( el, Color );
865  }
866 
867  /* X */
868  scratchNode = node.namedItem( "x" );
869 
870  if ( scratchNode.isNull() )
871  {
872  QgsDebugMsg( "couldn't find QgsLabel ``x'' attribute" );
873  }
874  else
875  {
876  el = scratchNode.toElement();
877  readLabelField( el, XCoordinate );
878  }
879 
880  /* Y */
881  scratchNode = node.namedItem( "y" );
882 
883  if ( scratchNode.isNull() )
884  {
885  QgsDebugMsg( "couldn't find QgsLabel ``y'' attribute" );
886  }
887  else
888  {
889  el = scratchNode.toElement();
890  readLabelField( el, YCoordinate );
891  }
892 
893 
894  /* X,Y offset */
895  scratchNode = node.namedItem( "offset" );
896 
897  if ( scratchNode.isNull() )
898  {
899  QgsDebugMsg( "couldn't find QgsLabel ``offset'' attribute" );
900  }
901  else
902  {
903  double xoffset, yoffset;
904 
905  el = scratchNode.toElement();
906 
907  type = QgsLabelAttributes::unitsCode( el.attribute( "units", "" ) );
908  xoffset = el.attribute( "x", "0.0" ).toDouble();
909  yoffset = el.attribute( "y", "0.0" ).toDouble();
910 
911  mLabelAttributes->setOffset( xoffset, yoffset, type );
912  readLabelField( el, XOffset, "xfield" );
913  readLabelField( el, YOffset, "yfield" );
914  }
915 
916  /* Angle */
917  scratchNode = node.namedItem( "angle" );
918 
919  if ( scratchNode.isNull() )
920  {
921  QgsDebugMsg( "couldn't find QgsLabel ``angle'' attribute" );
922  }
923  else
924  {
925  el = scratchNode.toElement();
926  mLabelAttributes->setAngle( el.attribute( "value", "0.0" ).toDouble() );
927  readLabelField( el, Angle );
928  mLabelAttributes->setAutoAngle( el.attribute( "auto", "0" ) == "1" );
929  }
930 
931  /* Alignment */
932  scratchNode = node.namedItem( "alignment" );
933 
934  if ( scratchNode.isNull() )
935  {
936  QgsDebugMsg( "couldn't find QgsLabel ``alignment'' attribute" );
937  }
938  else
939  {
940  el = scratchNode.toElement();
941  mLabelAttributes->setAlignment( QgsLabelAttributes::alignmentCode( el.attribute( "value", "" ) ) );
942  readLabelField( el, Alignment );
943  }
944 
945 
946  // Buffer
947  scratchNode = node.namedItem( "buffercolor" );
948 
949  if ( scratchNode.isNull() )
950  {
951  QgsDebugMsg( "couldn't find QgsLabel ``buffercolor'' attribute" );
952  }
953  else
954  {
955  el = scratchNode.toElement();
956 
957  red = el.attribute( "red", "0" ).toInt();
958  green = el.attribute( "green", "0" ).toInt();
959  blue = el.attribute( "blue", "0" ).toInt();
960 
961  mLabelAttributes->setBufferColor( QColor( red, green, blue ) );
962  readLabelField( el, BufferColor );
963  }
964 
965  scratchNode = node.namedItem( "buffersize" );
966 
967  if ( scratchNode.isNull() )
968  {
969  QgsDebugMsg( "couldn't find QgsLabel ``bffersize'' attribute" );
970  }
971  else
972  {
973  el = scratchNode.toElement();
974 
975  type = QgsLabelAttributes::unitsCode( el.attribute( "units", "" ) );
976  mLabelAttributes->setBufferSize( el.attribute( "value", "0.0" ).toDouble(), type );
977  readLabelField( el, BufferSize );
978  }
979 
980  scratchNode = node.namedItem( "bufferenabled" );
981 
982  if ( scratchNode.isNull() )
983  {
984  QgsDebugMsg( "couldn't find QgsLabel ``bufferenabled'' attribute" );
985  }
986  else
987  {
988  el = scratchNode.toElement();
989 
990  mLabelAttributes->setBufferEnabled(( bool )el.attribute( "on", "0" ).toInt() );
991  readLabelField( el, BufferEnabled );
992  }
993 
994  scratchNode = node.namedItem( "multilineenabled" );
995 
996  if ( scratchNode.isNull() )
997  {
998  QgsDebugMsg( "couldn't find QgsLabel ``multilineenabled'' attribute" );
999  }
1000  else
1001  {
1002  el = scratchNode.toElement();
1003 
1004  mLabelAttributes->setMultilineEnabled(( bool )el.attribute( "on", "0" ).toInt() );
1005  readLabelField( el, MultilineEnabled );
1006  }
1007 
1008  scratchNode = node.namedItem( "selectedonly" );
1009 
1010  if ( scratchNode.isNull() )
1011  {
1012  QgsDebugMsg( "couldn't find QgsLabel ``selectedonly'' attribute" );
1013  }
1014  else
1015  {
1016  el = scratchNode.toElement();
1017  mLabelAttributes->setSelectedOnly(( bool )el.attribute( "on", "0" ).toInt() );
1018  }
1019 
1020 } // QgsLabel::readXML()
1021 
1022 
1023 
1024 void QgsLabel::writeXML( QDomNode & layer_node, QDomDocument & document ) const
1025 {
1026  QDomElement labelattributes = document.createElement( "labelattributes" );
1027 
1028  // Text
1029  QDomElement label = document.createElement( "label" );
1030  label.setAttribute( "text", mLabelAttributes->text() );
1031  if ( mLabelAttributes->textIsSet() && mLabelFieldIdx[Text] != -1 )
1032  {
1033  label.setAttribute( "fieldname", labelField( Text ) );
1034  }
1035  else
1036  {
1037  label.setAttribute( "fieldname", "" );
1038  }
1039  labelattributes.appendChild( label );
1040 
1041  // Family
1042  QDomElement family = document.createElement( "family" );
1043  if ( mLabelAttributes->familyIsSet() && !mLabelAttributes->family().isNull() )
1044  {
1045  if ( mLabelFieldIdx[Family] != -1 )
1046  {
1047  family.setAttribute( "name", mLabelAttributes->family() );
1048  family.setAttribute( "fieldname", labelField( Family ) );
1049  }
1050  else
1051  {
1052  family.setAttribute( "name", mLabelAttributes->family() );
1053  family.setAttribute( "fieldname", "" );
1054  }
1055  }
1056  else
1057  {
1058  family.setAttribute( "name", "Arial" );
1059  family.setAttribute( "fieldname", "" );
1060  }
1061  labelattributes.appendChild( family );
1062 
1063  // size and units
1064  QDomElement size = document.createElement( "size" );
1065  size.setAttribute( "value", QString::number( mLabelAttributes->size() ) );
1066  if ( mLabelAttributes->sizeIsSet() )
1067  {
1068  if ( mLabelFieldIdx[Size] != -1 )
1069  {
1070  if ( mLabelFieldIdx[SizeType] != -1 )
1071  {
1072  size.setAttribute( "unitfieldname", labelField( SizeType ) );
1073  }
1074  else
1075  {
1076  size.setAttribute( "units", QgsLabelAttributes::unitsName( mLabelAttributes->sizeType() ) );
1077  }
1078  size.setAttribute( "fieldname", labelField( Size ) );
1079  }
1080  else
1081  {
1082  size.setAttribute( "units", QgsLabelAttributes::unitsName( mLabelAttributes->sizeType() ) );
1083  size.setAttribute( "fieldname", "" );
1084  }
1085  }
1086  else
1087  {
1088  size.setAttribute( "value", "12" );
1089  size.setAttribute( "units", "Points" );
1090  size.setAttribute( "fieldname", "" );
1091  }
1092  labelattributes.appendChild( size );
1093 
1094  // bold
1095  QDomElement bold = document.createElement( "bold" );
1096  if ( mLabelAttributes->boldIsSet() )
1097  {
1098  bold.setAttribute( "on", mLabelAttributes->bold() );
1099  if ( mLabelFieldIdx[Bold] != -1 )
1100  {
1101  bold.setAttribute( "fieldname", labelField( Bold ) );
1102  }
1103  else
1104  {
1105  bold.setAttribute( "fieldname", "" );
1106  }
1107  }
1108  else
1109  {
1110  bold.setAttribute( "on", 0 );
1111  bold.setAttribute( "fieldname", 0 );
1112  }
1113  labelattributes.appendChild( bold );
1114 
1115  // italics
1116  QDomElement italic = document.createElement( "italic" );
1117  if ( mLabelAttributes->italicIsSet() )
1118  {
1119  italic.setAttribute( "on", mLabelAttributes->italic() );
1120  if ( mLabelFieldIdx[Italic] != -1 )
1121  {
1122  italic.setAttribute( "fieldname", labelField( Italic ) );
1123  }
1124  else
1125  {
1126  italic.setAttribute( "fieldname", "" );
1127  }
1128  }
1129  else
1130  {
1131  italic.setAttribute( "on", "0" );
1132  italic.setAttribute( "fieldname", "" );
1133  }
1134  labelattributes.appendChild( italic );
1135 
1136  // underline
1137  QDomElement underline = document.createElement( "underline" );
1138  if ( mLabelAttributes->underlineIsSet() )
1139  {
1140  underline.setAttribute( "on", mLabelAttributes->underline() );
1141  if ( mLabelFieldIdx[Underline] != -1 )
1142  {
1143  underline.setAttribute( "fieldname", labelField( Underline ) );
1144  }
1145  else
1146  {
1147  underline.setAttribute( "fieldname", "" );
1148  }
1149  }
1150  else
1151  {
1152  underline.setAttribute( "on", 0 );
1153  underline.setAttribute( "fieldname", "" );
1154  }
1155  labelattributes.appendChild( underline );
1156 
1157  // strikeout
1158  QDomElement strikeOut = document.createElement( "strikeout" );
1159  if ( mLabelAttributes->strikeOutIsSet() )
1160  {
1161  strikeOut.setAttribute( "on", mLabelAttributes->strikeOut() );
1162  if ( mLabelFieldIdx[StrikeOut] != -1 )
1163  {
1164  strikeOut.setAttribute( "fieldname", labelField( StrikeOut ) );
1165  }
1166  else
1167  {
1168  strikeOut.setAttribute( "fieldname", "" );
1169  }
1170  }
1171  else
1172  {
1173  strikeOut.setAttribute( "on", 0 );
1174  strikeOut.setAttribute( "fieldname", "" );
1175  }
1176  labelattributes.appendChild( strikeOut );
1177 
1178  // color
1179  QDomElement color = document.createElement( "color" );
1180  if ( mLabelAttributes->colorIsSet() )
1181  {
1182  color.setAttribute( "red", mLabelAttributes->color().red() );
1183  color.setAttribute( "green", mLabelAttributes->color().green() );
1184  color.setAttribute( "blue", mLabelAttributes->color().blue() );
1185  if ( mLabelFieldIdx[Color] != -1 )
1186  {
1187  color.setAttribute( "fieldname", labelField( Color ) );
1188  }
1189  else
1190  {
1191  color.setAttribute( "fieldname", "" );
1192  }
1193  }
1194  else
1195  {
1196  color.setAttribute( "red", 0 );
1197  color.setAttribute( "green", 0 );
1198  color.setAttribute( "blue", 0 );
1199  color.setAttribute( "fieldname", "" );
1200  }
1201  labelattributes.appendChild( color );
1202 
1203  /* X */
1204  QDomElement x = document.createElement( "x" );
1205  if ( mLabelFieldIdx[XCoordinate] != -1 )
1206  {
1207  x.setAttribute( "fieldname", labelField( XCoordinate ) );
1208  }
1209  else
1210  {
1211  x.setAttribute( "fieldname", "" );
1212  }
1213  labelattributes.appendChild( x );
1214 
1215  /* Y */
1216  QDomElement y = document.createElement( "y" );
1217  if ( mLabelFieldIdx[YCoordinate] != -1 )
1218  {
1219  y.setAttribute( "fieldname", labelField( YCoordinate ) );
1220  }
1221  else
1222  {
1223  y.setAttribute( "fieldname", "" );
1224  }
1225  labelattributes.appendChild( y );
1226 
1227  // offset
1228  if ( mLabelAttributes->offsetIsSet() )
1229  {
1230  QDomElement offset = document.createElement( "offset" );
1231  offset.setAttribute( "units", QgsLabelAttributes::unitsName( mLabelAttributes->offsetType() ) );
1232  offset.setAttribute( "x", QString::number( mLabelAttributes->xOffset() ) );
1233  offset.setAttribute( "xfieldname", labelField( XOffset ) );
1234  offset.setAttribute( "y", QString::number( mLabelAttributes->yOffset() ) );
1235  offset.setAttribute( "yfieldname", labelField( YOffset ) );
1236  labelattributes.appendChild( offset );
1237  }
1238 
1239  // Angle
1240  QDomElement angle = document.createElement( "angle" );
1241  if ( mLabelAttributes->angleIsSet() )
1242  {
1243  angle.setAttribute( "value", QString::number( mLabelAttributes->angle() ) );
1244  if ( mLabelFieldIdx[Angle] != -1 )
1245  {
1246  angle.setAttribute( "fieldname", labelField( Angle ) );
1247  }
1248  else
1249  {
1250  angle.setAttribute( "fieldname", "" );
1251  }
1252  }
1253  else
1254  {
1255  angle.setAttribute( "value", "" );
1256  angle.setAttribute( "fieldname", "" );
1257  }
1258  angle.setAttribute( "auto", mLabelAttributes->angleIsAuto() ? "1" : "0" );
1259  labelattributes.appendChild( angle );
1260 
1261  // alignment
1262  if ( mLabelAttributes->alignmentIsSet() )
1263  {
1264  QDomElement alignment = document.createElement( "alignment" );
1265  alignment.setAttribute( "value", QgsLabelAttributes::alignmentName( mLabelAttributes->alignment() ) );
1266  alignment.setAttribute( "fieldname", labelField( Alignment ) );
1267  labelattributes.appendChild( alignment );
1268  }
1269 
1270  // buffer color
1271  QDomElement buffercolor = document.createElement( "buffercolor" );
1272  if ( mLabelAttributes->bufferColorIsSet() )
1273  {
1274  buffercolor.setAttribute( "red", mLabelAttributes->bufferColor().red() );
1275  buffercolor.setAttribute( "green", mLabelAttributes->bufferColor().green() );
1276  buffercolor.setAttribute( "blue", mLabelAttributes->bufferColor().blue() );
1277  if ( mLabelFieldIdx[BufferColor] != -1 )
1278  {
1279  buffercolor.setAttribute( "fieldname", labelField( BufferColor ) );
1280  }
1281  else
1282  {
1283  buffercolor.setAttribute( "fieldname", "" );
1284  }
1285  }
1286  else
1287  {
1288  buffercolor.setAttribute( "red", "" );
1289  buffercolor.setAttribute( "green", "" );
1290  buffercolor.setAttribute( "blue", "" );
1291  buffercolor.setAttribute( "fieldname", "" );
1292  }
1293  labelattributes.appendChild( buffercolor );
1294 
1295  // buffer size
1296  QDomElement buffersize = document.createElement( "buffersize" );
1297  if ( mLabelAttributes->bufferSizeIsSet() )
1298  {
1299  buffersize.setAttribute( "value", QString::number( mLabelAttributes->bufferSize() ) );
1300  buffersize.setAttribute( "units", QgsLabelAttributes::unitsName( mLabelAttributes->bufferSizeType() ) );
1301  if ( mLabelFieldIdx[BufferSize] != -1 )
1302  {
1303  buffersize.setAttribute( "fieldname", labelField( BufferSize ) );
1304  }
1305  else
1306  {
1307  buffersize.setAttribute( "fieldname", "" );
1308  }
1309  }
1310  else
1311  {
1312  buffersize.setAttribute( "value", "" );
1313  buffersize.setAttribute( "units", "" );
1314  buffersize.setAttribute( "fieldname", "" );
1315  }
1316  labelattributes.appendChild( buffersize );
1317 
1318  // buffer enabled
1319  QDomElement bufferenabled = document.createElement( "bufferenabled" );
1320  if ( mLabelAttributes->bufferEnabled() )
1321  {
1322  bufferenabled.setAttribute( "on", mLabelAttributes->bufferEnabled() );
1323  if ( mLabelFieldIdx[BufferEnabled] != -1 )
1324  {
1325  bufferenabled.setAttribute( "fieldname", labelField( BufferEnabled ) );
1326  }
1327  else
1328  {
1329  bufferenabled.setAttribute( "fieldname", "" );
1330  }
1331  }
1332  else
1333  {
1334  bufferenabled.setAttribute( "on", "" );
1335  bufferenabled.setAttribute( "fieldname", "" );
1336  }
1337  labelattributes.appendChild( bufferenabled );
1338 
1339  // multiline enabled
1340  QDomElement multilineenabled = document.createElement( "multilineenabled" );
1341  if ( mLabelAttributes->multilineEnabled() )
1342  {
1343  multilineenabled.setAttribute( "on", mLabelAttributes->multilineEnabled() );
1344  if ( mLabelFieldIdx[MultilineEnabled] != -1 )
1345  {
1346  multilineenabled.setAttribute( "fieldname", labelField( MultilineEnabled ) );
1347  }
1348  else
1349  {
1350  multilineenabled.setAttribute( "fieldname", "" );
1351  }
1352  }
1353  else
1354  {
1355  multilineenabled.setAttribute( "on", "" );
1356  multilineenabled.setAttribute( "fieldname", "" );
1357  }
1358  labelattributes.appendChild( multilineenabled );
1359 
1360  QDomElement selectedonly = document.createElement( "selectedonly" );
1361  if ( mLabelAttributes->selectedOnly() )
1362  {
1363  selectedonly.setAttribute( "on", mLabelAttributes->selectedOnly() );
1364  }
1365  else
1366  {
1367  selectedonly.setAttribute( "on", "" );
1368  }
1369  labelattributes.appendChild( selectedonly );
1370 
1371  layer_node.appendChild( labelattributes );
1372 }
1373 
1374 void QgsLabel::setScaleBasedVisibility( bool theVisibilityFlag )
1375 {
1376  mScaleBasedVisibility = theVisibilityFlag;
1377 }
1378 
1380 {
1381  return mScaleBasedVisibility;
1382 }
1383 
1384 void QgsLabel::setMinScale( float theMinScale )
1385 {
1386  mMinScale = theMinScale;
1387 }
1388 
1389 float QgsLabel::minScale() const
1390 {
1391  return mMinScale;
1392 }
1393 
1394 void QgsLabel::setMaxScale( float theMaxScale )
1395 {
1396  mMaxScale = theMaxScale;
1397 }
1398 
1399 float QgsLabel::maxScale() const
1400 {
1401  return mMaxScale;
1402 }
void setBufferColor(const QColor &color)
double bufferSize() const
void setMultilineEnabled(bool useMultiline)
void setBold(bool enable)
void setLabelField(int attr, int fieldIndex)
Set label field.
Definition: qgslabel.cpp:491
void setSize(double size, int type)
void setUnderline(bool enable)
void setSelectedOnly(bool selectedonly)
static int unitsCode(const QString &name)
void setFamily(const QString &family)
bool alignmentIsSet() const
size_t wkbSize() const
Returns the size of the WKB in asWkb().
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
float maxScale() const
Definition: qgslabel.cpp:1399
static QString alignmentName(int alignment)
QgsGeometry * geometry() const
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:112
QgsPoint transform(const QgsPoint &p) const
void renderLabel(QgsRenderContext &renderContext, QgsFeature &feature, bool selected, QgsLabelAttributes *classAttributes=0)
render label
Definition: qgslabel.cpp:72
Container of fields for a vector layer.
Definition: qgsfield.h:172
void setFields(const QgsFields &fields)
Set available fields.
Definition: qgslabel.cpp:481
QgsPoint transform(const QgsPoint &p, TransformDirection direction=ForwardTransform) const
double scaleFactor() const
WkbType
Used for symbology operations.
Definition: qgis.h:53
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
const QgsCoordinateTransform * coordinateTransform() const
float minScale() const
Definition: qgslabel.cpp:1389
double x() const
Definition: qgspoint.h:126
QgsLabelAttributes * labelAttributes()
Pointer to default attributes.
Definition: qgslabel.cpp:510
void setAutoAngle(bool state)
static QString unitsName(int units)
QColor bufferColor() const
static int alignmentCode(const QString &name)
const QColor & color() const
void setBufferSize(double size, int type)
void setScaleBasedVisibility(bool theVisibilityFlag)
Accessor and mutator for the scale based visilibility flag.
Definition: qgslabel.cpp:1374
#define M_PI
QList< int > QgsAttributeList
bool scaleBasedVisibility() const
Definition: qgslabel.cpp:1379
double rasterScaleFactor() const
int count() const
Return number of items.
Definition: qgsfield.h:214
QGis::WkbType wkbType() const
Returns type of wkb (point / linestring / polygon etc.)
QString labelField(int attr) const
label field
Definition: qgslabel.cpp:499
QgsFields & fields()
Available vector fields.
Definition: qgslabel.cpp:486
void setText(const QString &text)
void readXML(const QDomNode &node)
Reads the renderer configuration from an XML file.
Definition: qgslabel.cpp:731
bool multilineEnabled() const
void setAngle(double angle)
A class to represent a point.
Definition: qgspoint.h:63
void writeXML(QDomNode &label_node, QDomDocument &document) const
Writes the contents of the renderer to a configuration file.
Definition: qgslabel.cpp:1024
QString fieldValue(int attr, QgsFeature &feature)
Get field value if : 1) field name is not empty 2) field exists 3) value is defined otherwise returns...
Definition: qgslabel.cpp:66
bool bufferSizeIsSet() const
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
void setX(double x)
Definition: qgspoint.h:103
void setY(double y)
Definition: qgspoint.h:111
const QString family() const
void setStrikeOut(bool enable)
void addRequiredFields(QgsAttributeList &fields) const
add vector of required fields to existing list of fields
Definition: qgslabel.cpp:459
Contains information about the context of a rendering operation.
void setMaxScale(float theMaxScale)
Accessor and mutator for the maximum scale member.
Definition: qgslabel.cpp:1394
void setAlignment(int alignment)
QPainter * painter()
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:230
void setOffset(double x, double y, int type)
QgsLabel(const QgsFields &fields)
Definition: qgslabel.cpp:47
bool strikeOutIsSet() const
const QgsMapToPixel & mapToPixel() const
double y() const
Definition: qgspoint.h:134
~QgsLabel()
Definition: qgslabel.cpp:61
bool underlineIsSet() const
void setMinScale(float theMinScale)
Accessor and mutator for the minimum scale member.
Definition: qgslabel.cpp:1384
Custom exception class for Coordinate Reference System related exceptions.
A class to store attributes needed for label rendering.
void setColor(const QColor &color)
double size
Definition: qgssvgcache.cpp:77
void setBufferEnabled(bool useBufferFlag)
const unsigned char * asWkb() const
Returns the buffer containing this geometry in WKB format.
bool bufferColorIsSet() const
void setItalic(bool enable)
const QString text() const