Quantum GIS API Documentation  1.8
src/core/qgspallabeling.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   qgspallabeling.cpp
00003   Smart labeling for vector layers
00004   -------------------
00005          begin                : June 2009
00006          copyright            : (C) Martin Dobias
00007          email                : wonder.sk at gmail.com
00008 
00009  ***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgspallabeling.h"
00019 
00020 #include <list>
00021 
00022 #include <pal/pal.h>
00023 #include <pal/feature.h>
00024 #include <pal/layer.h>
00025 #include <pal/palgeometry.h>
00026 #include <pal/palexception.h>
00027 #include <pal/problem.h>
00028 #include <pal/labelposition.h>
00029 
00030 #include <geos_c.h>
00031 
00032 #include <cmath>
00033 
00034 #include <QByteArray>
00035 #include <QString>
00036 #include <QFontMetrics>
00037 #include <QTime>
00038 #include <QPainter>
00039 
00040 #include "qgsdiagram.h"
00041 #include "qgsdiagramrendererv2.h"
00042 #include "qgslabelsearchtree.h"
00043 #include "qgsexpression.h"
00044 
00045 #include <qgslogger.h>
00046 #include <qgsvectorlayer.h>
00047 #include <qgsmaplayerregistry.h>
00048 #include <qgsvectordataprovider.h>
00049 #include <qgsgeometry.h>
00050 #include <qgsmaprenderer.h>
00051 #include <QMessageBox>
00052 
00053 using namespace pal;
00054 
00055 
00056 class QgsPalGeometry : public PalGeometry
00057 {
00058   public:
00059     QgsPalGeometry( QgsFeatureId id, QString text, GEOSGeometry* g )
00060         : mG( g )
00061         , mText( text )
00062         , mId( id )
00063         , mInfo( NULL )
00064         , mIsDiagram( false )
00065     {
00066       mStrId = FID_TO_STRING( id ).toAscii();
00067     }
00068 
00069     ~QgsPalGeometry()
00070     {
00071       if ( mG )
00072         GEOSGeom_destroy( mG );
00073       delete mInfo;
00074     }
00075 
00076     // getGeosGeometry + releaseGeosGeometry is called twice: once when adding, second time when labeling
00077 
00078     GEOSGeometry* getGeosGeometry()
00079     {
00080       return mG;
00081     }
00082     void releaseGeosGeometry( GEOSGeometry* /*geom*/ )
00083     {
00084       // nothing here - we'll delete the geometry in destructor
00085     }
00086 
00087     const char* strId() { return mStrId.data(); }
00088     QString text() { return mText; }
00089 
00090     pal::LabelInfo* info( QFontMetricsF* fm, const QgsMapToPixel* xform, double fontScale )
00091     {
00092       if ( mInfo )
00093         return mInfo;
00094 
00095       // create label info!
00096       QgsPoint ptZero = xform->toMapCoordinates( 0, 0 );
00097       QgsPoint ptSize = xform->toMapCoordinatesF( 0.0, -fm->height() / fontScale );
00098 
00099       mInfo = new pal::LabelInfo( mText.count(), ptSize.y() - ptZero.y() );
00100       for ( int i = 0; i < mText.count(); i++ )
00101       {
00102         mInfo->char_info[i].chr = mText[i].unicode();
00103         ptSize = xform->toMapCoordinatesF( fm->width( mText[i] ) / fontScale , 0.0 );
00104         mInfo->char_info[i].width = ptSize.x() - ptZero.x();
00105       }
00106       return mInfo;
00107     }
00108 
00109     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& dataDefinedValues() const { return mDataDefinedValues; }
00110     void addDataDefinedValue( QgsPalLayerSettings::DataDefinedProperties p, QVariant v ) { mDataDefinedValues.insert( p, v ); }
00111 
00112     void setIsDiagram( bool d ) { mIsDiagram = d; }
00113     bool isDiagram() const { return mIsDiagram; }
00114 
00115     void addDiagramAttribute( int index, QVariant value ) { mDiagramAttributes.insert( index, value ); }
00116     const QgsAttributeMap& diagramAttributes() { return mDiagramAttributes; }
00117 
00118   protected:
00119     GEOSGeometry* mG;
00120     QString mText;
00121     QByteArray mStrId;
00122     QgsFeatureId mId;
00123     LabelInfo* mInfo;
00124     bool mIsDiagram;
00126     QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > mDataDefinedValues;
00127 
00129     QgsAttributeMap mDiagramAttributes;
00130 };
00131 
00132 // -------------
00133 
00134 QgsPalLayerSettings::QgsPalLayerSettings()
00135     : palLayer( NULL ), fontMetrics( NULL ), ct( NULL ), extentGeom( NULL ), expression( NULL )
00136 {
00137   placement = AroundPoint;
00138   placementFlags = 0;
00139   //textFont = QFont();
00140   textColor = Qt::black;
00141   enabled = false;
00142   priority = 5;
00143   obstacle = true;
00144   dist = 0;
00145   scaleMin = 0;
00146   scaleMax = 0;
00147   bufferSize = 1;
00148   bufferColor = Qt::white;
00149   formatNumbers = false;
00150   decimals = 3;
00151   plusSign = false;
00152   labelPerPart = false;
00153   mergeLines = false;
00154   minFeatureSize = 0.0;
00155   vectorScaleFactor = 1.0;
00156   rasterCompressFactor = 1.0;
00157   addDirectionSymbol = false;
00158   fontSizeInMapUnits = false;
00159   distInMapUnits = false;
00160   wrapChar = "";
00161 }
00162 
00163 QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
00164 {
00165   // copy only permanent stuff
00166   fieldName = s.fieldName;
00167   isExpression = s.isExpression;
00168   placement = s.placement;
00169   placementFlags = s.placementFlags;
00170   textFont = s.textFont;
00171   textColor = s.textColor;
00172   enabled = s.enabled;
00173   priority = s.priority;
00174   obstacle = s.obstacle;
00175   dist = s.dist;
00176   scaleMin = s.scaleMin;
00177   scaleMax = s.scaleMax;
00178   bufferSize = s.bufferSize;
00179   bufferColor = s.bufferColor;
00180   formatNumbers = s.formatNumbers;
00181   decimals = s.decimals;
00182   plusSign = s.plusSign;
00183   labelPerPart = s.labelPerPart;
00184   mergeLines = s.mergeLines;
00185   minFeatureSize = s.minFeatureSize;
00186   vectorScaleFactor = s.vectorScaleFactor;
00187   rasterCompressFactor = s.rasterCompressFactor;
00188   addDirectionSymbol = s.addDirectionSymbol;
00189   fontSizeInMapUnits = s.fontSizeInMapUnits;
00190   distInMapUnits = s.distInMapUnits;
00191   wrapChar = s.wrapChar;
00192 
00193   dataDefinedProperties = s.dataDefinedProperties;
00194   fontMetrics = NULL;
00195   ct = NULL;
00196   extentGeom = NULL;
00197   expression = NULL;
00198 }
00199 
00200 
00201 QgsPalLayerSettings::~QgsPalLayerSettings()
00202 {
00203   // pal layer is deleted internally in PAL
00204 
00205   delete fontMetrics;
00206   delete ct;
00207   delete expression;
00208   delete extentGeom;
00209 }
00210 
00211 QgsExpression* QgsPalLayerSettings::getLabelExpression()
00212 {
00213   if ( expression == NULL )
00214   {
00215     expression = new QgsExpression( fieldName );
00216   }
00217   return expression;
00218 }
00219 
00220 static QColor _readColor( QgsVectorLayer* layer, QString property )
00221 {
00222   int r = layer->customProperty( property + "R" ).toInt();
00223   int g = layer->customProperty( property + "G" ).toInt();
00224   int b = layer->customProperty( property + "B" ).toInt();
00225   return QColor( r, g, b );
00226 }
00227 
00228 static void _writeColor( QgsVectorLayer* layer, QString property, QColor color )
00229 {
00230   layer->setCustomProperty( property + "R", color.red() );
00231   layer->setCustomProperty( property + "G", color.green() );
00232   layer->setCustomProperty( property + "B", color.blue() );
00233 }
00234 
00235 static void _writeDataDefinedPropertyMap( QgsVectorLayer* layer, const QMap< QgsPalLayerSettings::DataDefinedProperties, int >& propertyMap )
00236 {
00237   if ( !layer )
00238   {
00239     return;
00240   }
00241 
00242   for ( int i = 0; i < 15; ++i )
00243   {
00244     QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator it = propertyMap.find(( QgsPalLayerSettings::DataDefinedProperties )i );
00245     QVariant propertyValue;
00246     if ( it == propertyMap.constEnd() )
00247     {
00248       propertyValue = QVariant(); //we cannot delete properties, so we just insert an invalid variant
00249     }
00250     else
00251     {
00252       propertyValue = *it;
00253     }
00254     layer->setCustomProperty( "labeling/dataDefinedProperty" + QString::number( i ), propertyValue );
00255   }
00256 }
00257 
00258 static void _readDataDefinedProperty( QgsVectorLayer* layer, QgsPalLayerSettings::DataDefinedProperties p,
00259                                       QMap< QgsPalLayerSettings::DataDefinedProperties, int >& propertyMap )
00260 {
00261   QVariant propertyField = layer->customProperty( "labeling/dataDefinedProperty" + QString::number( p ) );
00262   bool conversionOk;
00263   int fieldIndex;
00264 
00265   if ( propertyField.isValid() )
00266   {
00267     fieldIndex = propertyField.toInt( &conversionOk );
00268     if ( conversionOk )
00269     {
00270       propertyMap.insert( p, fieldIndex );
00271     }
00272   }
00273 }
00274 
00275 static void _readDataDefinedPropertyMap( QgsVectorLayer* layer, QMap< QgsPalLayerSettings::DataDefinedProperties, int >& propertyMap )
00276 {
00277   if ( !layer )
00278   {
00279     return;
00280   }
00281 
00282   _readDataDefinedProperty( layer, QgsPalLayerSettings::Size, propertyMap );
00283   _readDataDefinedProperty( layer, QgsPalLayerSettings::Color, propertyMap );
00284   _readDataDefinedProperty( layer, QgsPalLayerSettings::Bold, propertyMap );
00285   _readDataDefinedProperty( layer, QgsPalLayerSettings::Italic, propertyMap );
00286   _readDataDefinedProperty( layer, QgsPalLayerSettings::Underline, propertyMap );
00287   _readDataDefinedProperty( layer, QgsPalLayerSettings::Strikeout, propertyMap );
00288   _readDataDefinedProperty( layer, QgsPalLayerSettings::Family, propertyMap );
00289   _readDataDefinedProperty( layer, QgsPalLayerSettings::BufferSize, propertyMap );
00290   _readDataDefinedProperty( layer, QgsPalLayerSettings::BufferColor, propertyMap );
00291   _readDataDefinedProperty( layer,  QgsPalLayerSettings::PositionX, propertyMap );
00292   _readDataDefinedProperty( layer,  QgsPalLayerSettings::PositionY, propertyMap );
00293   _readDataDefinedProperty( layer,  QgsPalLayerSettings::Hali, propertyMap );
00294   _readDataDefinedProperty( layer,  QgsPalLayerSettings::Vali, propertyMap );
00295   _readDataDefinedProperty( layer, QgsPalLayerSettings::LabelDistance, propertyMap );
00296   _readDataDefinedProperty( layer, QgsPalLayerSettings::Rotation, propertyMap );
00297 }
00298 
00299 void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
00300 {
00301   if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) )
00302     return; // there's no information available
00303 
00304   fieldName = layer->customProperty( "labeling/fieldName" ).toString();
00305   isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
00306   placement = ( Placement ) layer->customProperty( "labeling/placement" ).toInt();
00307   placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
00308   QString fontFamily = layer->customProperty( "labeling/fontFamily" ).toString();
00309   double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
00310   int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
00311   bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
00312   textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
00313   textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
00314   textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
00315   textFont.setPointSizeF( fontSize ); //double precision needed because of map units
00316   textColor = _readColor( layer, "labeling/textColor" );
00317   enabled = layer->customProperty( "labeling/enabled" ).toBool();
00318   priority = layer->customProperty( "labeling/priority" ).toInt();
00319   obstacle = layer->customProperty( "labeling/obstacle" ).toBool();
00320   dist = layer->customProperty( "labeling/dist" ).toDouble();
00321   scaleMin = layer->customProperty( "labeling/scaleMin" ).toInt();
00322   scaleMax = layer->customProperty( "labeling/scaleMax" ).toInt();
00323   bufferSize = layer->customProperty( "labeling/bufferSize" ).toDouble();
00324   bufferColor = _readColor( layer, "labeling/bufferColor" );
00325   formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
00326   decimals = layer->customProperty( "labeling/decimals" ).toInt();
00327   plusSign = layer->customProperty( "labeling/plussign" ).toInt();
00328   labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
00329   mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
00330   addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
00331   minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
00332   fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
00333   distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
00334   wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
00335   _readDataDefinedPropertyMap( layer, dataDefinedProperties );
00336 }
00337 
00338 void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
00339 {
00340   // this is a mark that labeling information is present
00341   layer->setCustomProperty( "labeling", "pal" );
00342 
00343   layer->setCustomProperty( "labeling/fieldName", fieldName );
00344   layer->setCustomProperty( "labeling/isExpression", isExpression );
00345   layer->setCustomProperty( "labeling/placement", placement );
00346   layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
00347 
00348   layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
00349   layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
00350   layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
00351   layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
00352   layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
00353   layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
00354 
00355   _writeColor( layer, "labeling/textColor", textColor );
00356   layer->setCustomProperty( "labeling/enabled", enabled );
00357   layer->setCustomProperty( "labeling/priority", priority );
00358   layer->setCustomProperty( "labeling/obstacle", obstacle );
00359   layer->setCustomProperty( "labeling/dist", dist );
00360   layer->setCustomProperty( "labeling/scaleMin", scaleMin );
00361   layer->setCustomProperty( "labeling/scaleMax", scaleMax );
00362   layer->setCustomProperty( "labeling/bufferSize", bufferSize );
00363   _writeColor( layer, "labeling/bufferColor", bufferColor );
00364   layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
00365   layer->setCustomProperty( "labeling/decimals", decimals );
00366   layer->setCustomProperty( "labeling/plussign", plusSign );
00367   layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
00368   layer->setCustomProperty( "labeling/mergeLines", mergeLines );
00369   layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
00370   layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
00371   layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
00372   layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
00373   layer->setCustomProperty( "labeling/wrapChar", wrapChar );
00374   _writeDataDefinedPropertyMap( layer, dataDefinedProperties );
00375 }
00376 
00377 void QgsPalLayerSettings::setDataDefinedProperty( DataDefinedProperties p, int attributeIndex )
00378 {
00379   dataDefinedProperties.insert( p, attributeIndex );
00380 }
00381 
00382 void QgsPalLayerSettings::removeDataDefinedProperty( DataDefinedProperties p )
00383 {
00384   dataDefinedProperties.remove( p );
00385 }
00386 
00387 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, QgsGeometry* geom, double minSize ) const
00388 {
00389   if ( minSize <= 0 )
00390   {
00391     return true;
00392   }
00393 
00394   if ( !geom )
00395   {
00396     return false;
00397   }
00398 
00399   QGis::GeometryType featureType = geom->type();
00400   if ( featureType == QGis::Point ) //minimum size does not apply to point features
00401   {
00402     return true;
00403   }
00404 
00405   double mapUnitsPerMM = ct.mapToPixel().mapUnitsPerPixel() * ct.scaleFactor();
00406   if ( featureType == QGis::Line )
00407   {
00408     double length = geom->length();
00409     if ( length >= 0.0 )
00410     {
00411       return ( length >= ( minSize * mapUnitsPerMM ) );
00412     }
00413   }
00414   else if ( featureType == QGis::Polygon )
00415   {
00416     double area = geom->area();
00417     if ( area >= 0.0 )
00418     {
00419       return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
00420     }
00421   }
00422   return true; //should never be reached. Return true in this case to label such geometries anyway.
00423 }
00424 
00425 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY )
00426 {
00427   if ( !fm )
00428   {
00429     return;
00430   }
00431 
00432   //consider the space needed for the direction symbol
00433   if ( addDirectionSymbol && placement == QgsPalLayerSettings::Line )
00434   {
00435     text.append( ">" );
00436   }
00437 
00438   double w, h;
00439   QStringList multiLineSplit;
00440   if ( !wrapChar.isEmpty() )
00441     multiLineSplit = text.split( wrapChar );
00442   else
00443     multiLineSplit = text.split( "\n" );
00444 
00445   h = fm->height() * multiLineSplit.size() / rasterCompressFactor;
00446   w = 0;
00447   for ( int i = 0; i < multiLineSplit.size(); ++i )
00448   {
00449     double width = fm->width( multiLineSplit.at( i ) );
00450     if ( width > w )
00451     {
00452       w = width;
00453     }
00454   }
00455   w /= rasterCompressFactor;
00456   QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
00457 
00458   labelX = qAbs( ptSize.x() - ptZero.x() );
00459   labelY = qAbs( ptSize.y() - ptZero.y() );
00460 }
00461 
00462 void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer,  QgsFeature& f, const QgsRenderContext& context )
00463 {
00464   QString labelText;
00465   // Check to see if we are a expression string.
00466   if ( isExpression )
00467   {
00468     QgsExpression* exp = getLabelExpression();
00469     if ( exp->hasParserError() )
00470     {
00471       QgsDebugMsg( "Expression parser error:" + exp->parserErrorString() );
00472       return;
00473     }
00474     QVariant result = exp->evaluate( &f, layer->pendingFields() );
00475     if ( exp->hasEvalError() )
00476     {
00477       QgsDebugMsg( "Expression parser eval error:" + exp->evalErrorString() );
00478       return;
00479     }
00480     labelText  = result.toString();
00481   }
00482   else if ( formatNumbers == true && ( f.attributeMap()[fieldIndex].type() == QVariant::Int ||
00483                                        f.attributeMap()[fieldIndex].type() == QVariant::Double ) )
00484   {
00485     QString numberFormat;
00486     double d = f.attributeMap()[fieldIndex].toDouble();
00487     if ( d > 0 && plusSign == true )
00488     {
00489       numberFormat.append( "+" );
00490     }
00491     numberFormat.append( "%1" );
00492     labelText = numberFormat.arg( d, 0, 'f', decimals );
00493   }
00494   else
00495   {
00496     labelText = f.attributeMap()[fieldIndex].toString();
00497   }
00498 
00499   double labelX, labelY; // will receive label size
00500   QFont labelFont = textFont;
00501 
00502   //data defined label size?
00503   QMap< DataDefinedProperties, int >::const_iterator it = dataDefinedProperties.find( QgsPalLayerSettings::Size );
00504   if ( it != dataDefinedProperties.constEnd() )
00505   {
00506     //find out size
00507     QVariant size = f.attributeMap().value( *it );
00508     if ( size.isValid() )
00509     {
00510       double sizeDouble = size.toDouble();
00511       if ( sizeDouble <= 0 )
00512       {
00513         return;
00514       }
00515       labelFont.setPixelSize( sizeToPixel( sizeDouble, context ) );
00516     }
00517     QFontMetricsF labelFontMetrics( labelFont );
00518     calculateLabelSize( &labelFontMetrics, labelText, labelX, labelY );
00519   }
00520   else
00521   {
00522     calculateLabelSize( fontMetrics, labelText, labelX, labelY );
00523   }
00524 
00525   QgsGeometry* geom = f.geometry();
00526   if ( !geom )
00527   {
00528     return;
00529   }
00530 
00531   if ( ct ) // reproject the geometry if necessary
00532     geom->transform( *ct );
00533 
00534   if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) )
00535   {
00536     return;
00537   }
00538 
00539   // CLIP the geometry if it is bigger than the extent
00540   QgsGeometry* geomClipped = NULL;
00541   GEOSGeometry* geos_geom;
00542   bool do_clip = !extentGeom->contains( geom );
00543   if ( do_clip )
00544   {
00545     geomClipped = geom->intersection( extentGeom ); // creates new geometry
00546     if ( !geomClipped )
00547     {
00548       return;
00549     }
00550     geos_geom = geomClipped->asGeos();
00551   }
00552   else
00553   {
00554     geos_geom = geom->asGeos();
00555   }
00556 
00557   if ( geos_geom == NULL )
00558     return; // invalid geometry
00559   GEOSGeometry* geos_geom_clone = GEOSGeom_clone( geos_geom );
00560   if ( do_clip )
00561     delete geomClipped;
00562 
00563   //data defined position / alignment / rotation?
00564   bool dataDefinedPosition = false;
00565   bool dataDefinedRotation = false;
00566   double xPos = 0.0, yPos = 0.0, angle = 0.0;
00567   bool ddXPos, ddYPos;
00568 
00569   QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX );
00570   if ( dPosXIt != dataDefinedProperties.constEnd() )
00571   {
00572     QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY );
00573     if ( dPosYIt != dataDefinedProperties.constEnd() )
00574     {
00575       //data defined position. But field values could be NULL -> positions will be generated by PAL
00576       xPos = f.attributeMap().value( *dPosXIt ).toDouble( &ddXPos );
00577       yPos = f.attributeMap().value( *dPosYIt ).toDouble( &ddYPos );
00578 
00579       if ( ddXPos && ddYPos )
00580       {
00581         dataDefinedPosition = true;
00582         //x/y shift in case of alignment
00583         double xdiff = 0;
00584         double ydiff = 0;
00585 
00586         //horizontal alignment
00587         QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali );
00588         if ( haliIt != dataDefinedProperties.end() )
00589         {
00590           QString haliString = f.attributeMap().value( *haliIt ).toString();
00591           if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
00592           {
00593             xdiff -= labelX / 2.0;
00594           }
00595           else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
00596           {
00597             xdiff -= labelX;
00598           }
00599         }
00600 
00601         //vertical alignment
00602         QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali );
00603         if ( valiIt != dataDefinedProperties.constEnd() )
00604         {
00605           QString valiString = f.attributeMap().value( *valiIt ).toString();
00606           if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
00607           {
00608             if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
00609             {
00610               ydiff -= labelY;
00611             }
00612             else
00613             {
00614               QFontMetrics labelFontMetrics( labelFont );
00615               double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
00616 
00617               if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
00618               {
00619                 ydiff -= labelY * descentRatio;
00620               }
00621               else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
00622               {
00623                 ydiff -= labelY * descentRatio;
00624                 ydiff -= labelY * 0.5 * ( 1 - descentRatio );
00625               }
00626             }
00627           }
00628         }
00629 
00630         //data defined rotation?
00631         QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation );
00632         if ( rotIt != dataDefinedProperties.constEnd() )
00633         {
00634           dataDefinedRotation = true;
00635           angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180;
00636           //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
00637           double xd = xdiff * cos( angle ) - ydiff * sin( angle );
00638           double yd = xdiff * sin( angle ) + ydiff * cos( angle );
00639           xdiff = xd;
00640           ydiff = yd;
00641         }
00642 
00643         //project xPos and yPos from layer to map CRS
00644         double z = 0;
00645         if ( ct )
00646         {
00647           ct->transformInPlace( xPos, yPos, z );
00648         }
00649 
00650         yPos += ydiff;
00651         xPos += xdiff;
00652 
00653       }
00654     }
00655   }
00656 
00657   QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), labelText, geos_geom_clone );
00658 
00659   // record the created geometry - it will be deleted at the end.
00660   geometries.append( lbl );
00661 
00662   //  feature to the layer
00663   try
00664   {
00665     if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
00666                                      xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation ) )
00667       return;
00668   }
00669   catch ( std::exception &e )
00670   {
00671     Q_UNUSED( e );
00672     QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( f.id() ) + QString::fromLatin1( e.what() ) );
00673     return;
00674   }
00675 
00676   // TODO: only for placement which needs character info
00677   pal::Feature* feat = palLayer->getFeature( lbl->strId() );
00678   feat->setLabelInfo( lbl->info( fontMetrics, xform, rasterCompressFactor ) );
00679 
00680   // TODO: allow layer-wide feature dist in PAL...?
00681 
00682   //data defined label-feature distance?
00683   double distance = dist;
00684   QMap< DataDefinedProperties, int >::const_iterator dDistIt = dataDefinedProperties.find( QgsPalLayerSettings::LabelDistance );
00685   if ( dDistIt != dataDefinedProperties.constEnd() )
00686   {
00687     distance = f.attributeMap().value( *dDistIt ).toDouble();
00688   }
00689 
00690   if ( distance != 0 )
00691   {
00692     if ( distInMapUnits ) //convert distance from mm/map units to pixels
00693     {
00694       distance /= context.mapToPixel().mapUnitsPerPixel();
00695     }
00696     else //mm
00697     {
00698       distance *= vectorScaleFactor;
00699     }
00700     feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
00701   }
00702 
00703   //add parameters for data defined labeling to QgsPalGeometry
00704   QMap< DataDefinedProperties, int >::const_iterator dIt = dataDefinedProperties.constBegin();
00705   for ( ; dIt != dataDefinedProperties.constEnd(); ++dIt )
00706   {
00707     lbl->addDataDefinedValue( dIt.key(), f.attributeMap()[dIt.value()] );
00708   }
00709 }
00710 
00711 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c ) const
00712 {
00713   double pixelSize;
00714   if ( fontSizeInMapUnits )
00715   {
00716     pixelSize = size / c.mapToPixel().mapUnitsPerPixel() * c.rasterScaleFactor();
00717   }
00718   else //font size in points
00719   {
00720     // set font size from points to output size
00721     pixelSize = 0.3527 * size * c.scaleFactor() * c.rasterScaleFactor();
00722   }
00723   return ( int )( pixelSize + 0.5 );
00724 }
00725 
00726 
00727 // -------------
00728 
00729 QgsPalLabeling::QgsPalLabeling()
00730     : mMapRenderer( NULL ), mPal( NULL )
00731 {
00732 
00733   // find out engine defaults
00734   Pal p;
00735   mCandPoint = p.getPointP();
00736   mCandLine = p.getLineP();
00737   mCandPolygon = p.getPolyP();
00738 
00739   switch ( p.getSearch() )
00740   {
00741     case CHAIN: mSearch = Chain; break;
00742     case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
00743     case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
00744     case POPMUSIC_TABU_CHAIN: mSearch = Popmusic_Tabu_Chain; break;
00745     case FALP: mSearch = Falp; break;
00746   }
00747 
00748   mShowingCandidates = false;
00749   mShowingAllLabels = false;
00750 
00751   mLabelSearchTree = new QgsLabelSearchTree();
00752 }
00753 
00754 
00755 QgsPalLabeling::~QgsPalLabeling()
00756 {
00757   // make sure we've freed everything
00758   exit();
00759   delete mLabelSearchTree;
00760   mLabelSearchTree = NULL;
00761 }
00762 
00763 
00764 bool QgsPalLabeling::willUseLayer( QgsVectorLayer* layer )
00765 {
00766   QgsPalLayerSettings lyrTmp;
00767   lyrTmp.readFromLayer( layer );
00768   return lyrTmp.enabled;
00769 }
00770 
00771 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx )
00772 {
00773   QgsDebugMsg( "PREPARE LAYER" );
00774   Q_ASSERT( mMapRenderer != NULL );
00775 
00776   // start with a temporary settings class, find out labeling info
00777   QgsPalLayerSettings lyrTmp;
00778   lyrTmp.readFromLayer( layer );
00779 
00780   if ( !lyrTmp.enabled )
00781     return 0;
00782 
00783 
00784   int fldIndex = -1;
00785   if ( lyrTmp.isExpression )
00786   {
00787     if ( lyrTmp.fieldName.isEmpty() )
00788       return 0;
00789     QgsExpression exp( lyrTmp.fieldName );
00790     foreach( QString name, exp.referencedColumns() )
00791     {
00792       QgsDebugMsg( "REFERENCED COLUMN = " + name );
00793       fldIndex =  layer->fieldNameIndex( name );
00794       attrIndices.insert( fldIndex );
00795     }
00796 
00797   }
00798   else
00799   {
00800     // If we aren't an expression, we check to see if we can find the column.
00801     fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
00802     if ( fldIndex == -1 )
00803       return 0;
00804     attrIndices.insert( fldIndex );
00805   }
00806 
00807   //add indices of data defined fields
00808   QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator dIt = lyrTmp.dataDefinedProperties.constBegin();
00809   for ( ; dIt != lyrTmp.dataDefinedProperties.constEnd(); ++dIt )
00810   {
00811     attrIndices.insert( dIt.value() );
00812   }
00813 
00814   // add layer settings to the pallabeling hashtable: <QgsVectorLayer*, QgsPalLayerSettings>
00815   mActiveLayers.insert( layer, lyrTmp );
00816   // start using the reference to the layer in hashtable instead of local instance
00817   QgsPalLayerSettings& lyr = mActiveLayers[layer];
00818 
00819   // how to place the labels
00820   Arrangement arrangement;
00821   switch ( lyr.placement )
00822   {
00823     case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
00824     case QgsPalLayerSettings::OverPoint:   arrangement = P_POINT_OVER; break;
00825     case QgsPalLayerSettings::Line:        arrangement = P_LINE; break;
00826     case QgsPalLayerSettings::Curved:      arrangement = P_CURVED; break;
00827     case QgsPalLayerSettings::Horizontal:  arrangement = P_HORIZ; break;
00828     case QgsPalLayerSettings::Free:        arrangement = P_FREE; break;
00829     default: Q_ASSERT( "unsupported placement" && 0 ); return 0;
00830   }
00831 
00832   // create the pal layer
00833   double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0
00834   double min_scale = -1, max_scale = -1;
00835   if ( lyr.scaleMin != 0 && lyr.scaleMax != 0 )
00836   {
00837     min_scale = lyr.scaleMin;
00838     max_scale = lyr.scaleMax;
00839   }
00840 
00841   Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
00842                              min_scale, max_scale, arrangement,
00843                              METER, priority, lyr.obstacle, true, true );
00844 
00845   if ( lyr.placementFlags )
00846     l->setArrangementFlags( lyr.placementFlags );
00847 
00848   // set label mode (label per feature is the default)
00849   l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
00850 
00851   // set whether adjacent lines should be merged
00852   l->setMergeConnectedLines( lyr.mergeLines );
00853 
00854   lyr.textFont.setPixelSize( lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx ) );
00855 
00856   //raster and vector scale factors
00857   lyr.vectorScaleFactor = ctx.scaleFactor();
00858   lyr.rasterCompressFactor = ctx.rasterScaleFactor();
00859 
00860   // save the pal layer to our layer context (with some additional info)
00861   lyr.palLayer = l;
00862   lyr.fieldIndex = fldIndex;
00863   lyr.fontMetrics = new QFontMetricsF( lyr.textFont );
00864 
00865   lyr.xform = mMapRenderer->coordinateTransform();
00866   if ( mMapRenderer->hasCrsTransformEnabled() )
00867     lyr.ct = new QgsCoordinateTransform( layer->crs(), mMapRenderer->destinationCrs() );
00868   else
00869     lyr.ct = NULL;
00870   lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
00871   lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
00872 
00873   // rect for clipping
00874   lyr.extentGeom = QgsGeometry::fromRect( mMapRenderer->extent() );
00875 
00876   return 1; // init successful
00877 }
00878 
00879 int QgsPalLabeling::addDiagramLayer( QgsVectorLayer* layer, QgsDiagramLayerSettings *s )
00880 {
00881   Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), -1, -1, pal::Arrangement( s->placement ), METER, s->priority, s->obstacle, true, true );
00882   l->setArrangementFlags( s->placementFlags );
00883 
00884   s->palLayer = l;
00885   if ( mMapRenderer->hasCrsTransformEnabled() )
00886     s->ct = new QgsCoordinateTransform( layer->crs(), mMapRenderer->destinationCrs() );
00887   else
00888     s->ct = NULL;
00889   s->xform = mMapRenderer->coordinateTransform();
00890   mActiveDiagramLayers.insert( layer, *s );
00891   return 1;
00892 }
00893 
00894 void QgsPalLabeling::registerFeature( QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
00895 {
00896   QgsPalLayerSettings& lyr = mActiveLayers[layer];
00897   lyr.registerFeature( layer, f, context );
00898 }
00899 
00900 void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature& feat, const QgsRenderContext& context )
00901 {
00902   //get diagram layer settings, diagram renderer
00903   QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layer );
00904   if ( layerIt == mActiveDiagramLayers.constEnd() )
00905   {
00906     return;
00907   }
00908 
00909   //convert geom to geos
00910   QgsGeometry* geom = feat.geometry();
00911 
00912   if ( layerIt.value().ct && !willUseLayer( layer ) ) // reproject the geometry if feature not already transformed for labeling
00913   {
00914     geom->transform( *( layerIt.value().ct ) );
00915   }
00916 
00917   GEOSGeometry* geos_geom = geom->asGeos();
00918   if ( geos_geom == 0 )
00919   {
00920     return; // invalid geometry
00921   }
00922 
00923   //create PALGeometry with diagram = true
00924   QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone( geos_geom ) );
00925   lbl->setIsDiagram( true );
00926 
00927   // record the created geometry - it will be deleted at the end.
00928   layerIt.value().geometries.append( lbl );
00929 
00930   double diagramWidth = 0;
00931   double diagramHeight = 0;
00932   QgsDiagramRendererV2* dr = layerIt.value().renderer;
00933   if ( dr )
00934   {
00935     QSizeF diagSize = dr->sizeMapUnits( feat.attributeMap(), context );
00936     if ( diagSize.isValid() )
00937     {
00938       diagramWidth = diagSize.width();
00939       diagramHeight = diagSize.height();
00940     }
00941 
00942     //append the diagram attributes to lbl
00943     QList<int> diagramAttrib = dr->diagramAttributes();
00944     QList<int>::const_iterator diagAttIt = diagramAttrib.constBegin();
00945     for ( ; diagAttIt != diagramAttrib.constEnd(); ++diagAttIt )
00946     {
00947       lbl->addDiagramAttribute( *diagAttIt, feat.attributeMap()[*diagAttIt] );
00948     }
00949   }
00950 
00951   //  feature to the layer
00952   int ddColX = layerIt.value().xPosColumn;
00953   int ddColY = layerIt.value().yPosColumn;
00954   double ddPosX = 0.0;
00955   double ddPosY = 0.0;
00956   bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
00957   if ( ddPos )
00958   {
00959     bool posXOk, posYOk;
00960     //data defined diagram position is always centered
00961     ddPosX = feat.attributeMap()[ddColX].toDouble( &posXOk ) - diagramWidth / 2.0;
00962     ddPosY = feat.attributeMap()[ddColY].toDouble( &posYOk ) - diagramHeight / 2.0;
00963     if ( !posXOk || !posYOk )
00964     {
00965       ddPos = false;
00966     }
00967     else
00968     {
00969       const QgsCoordinateTransform* ct = layerIt.value().ct;
00970       if ( ct )
00971       {
00972         double z = 0;
00973         ct->transformInPlace( ddPosX, ddPosY, z );
00974       }
00975     }
00976   }
00977 
00978   try
00979   {
00980     if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos ) )
00981     {
00982       return;
00983     }
00984   }
00985   catch ( std::exception &e )
00986   {
00987     Q_UNUSED( e );
00988     QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( feat.id() ) + QString::fromLatin1( e.what() ) );
00989     return;
00990   }
00991 
00992   pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() );
00993   QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 );
00994   QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 );
00995   palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist );
00996 }
00997 
00998 
00999 void QgsPalLabeling::init( QgsMapRenderer* mr )
01000 {
01001   mMapRenderer = mr;
01002 
01003   // delete if exists already
01004   if ( mPal )
01005     delete mPal;
01006 
01007   mPal = new Pal;
01008 
01009   SearchMethod s;
01010   switch ( mSearch )
01011   {
01012     default:
01013     case Chain: s = CHAIN; break;
01014     case Popmusic_Tabu: s = POPMUSIC_TABU; break;
01015     case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
01016     case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
01017     case Falp: s = FALP; break;
01018   }
01019   mPal->setSearch( s );
01020 
01021   // set number of candidates generated per feature
01022   mPal->setPointP( mCandPoint );
01023   mPal->setLineP( mCandLine );
01024   mPal->setPolyP( mCandPolygon );
01025 
01026   mActiveLayers.clear();
01027   mActiveDiagramLayers.clear();
01028 }
01029 
01030 void QgsPalLabeling::exit()
01031 {
01032   delete mPal;
01033   mPal = NULL;
01034   mMapRenderer = NULL;
01035 }
01036 
01037 QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
01038 {
01039   QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
01040   for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
01041   {
01042     if ( lit.key() && lit.key()->id() == layerName )
01043     {
01044       return lit.value();
01045     }
01046   }
01047   return mInvalidLayerSettings;
01048 }
01049 
01050 
01051 void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
01052 {
01053   Q_ASSERT( mMapRenderer != NULL );
01054   QPainter* painter = context.painter();
01055   QgsRectangle extent = context.extent();
01056 
01057   if ( mLabelSearchTree )
01058   {
01059     mLabelSearchTree->clear();
01060   }
01061 
01062   QTime t;
01063   t.start();
01064 
01065   // do the labeling itself
01066   double scale = mMapRenderer->scale(); // scale denominator
01067   QgsRectangle r = extent;
01068   double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() };
01069 
01070   std::list<LabelPosition*>* labels;
01071   pal::Problem* problem;
01072   try
01073   {
01074     problem = mPal->extractProblem( scale, bbox );
01075   }
01076   catch ( std::exception& e )
01077   {
01078     Q_UNUSED( e );
01079     QgsDebugMsg( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ) );
01080     //mActiveLayers.clear(); // clean up
01081     return;
01082   }
01083 
01084   const QgsMapToPixel* xform = mMapRenderer->coordinateTransform();
01085 
01086   // draw rectangles with all candidates
01087   // this is done before actual solution of the problem
01088   // before number of candidates gets reduced
01089   mCandidates.clear();
01090   if ( mShowingCandidates && problem )
01091   {
01092     painter->setPen( QColor( 0, 0, 0, 64 ) );
01093     painter->setBrush( Qt::NoBrush );
01094     for ( int i = 0; i < problem->getNumFeatures(); i++ )
01095     {
01096       for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
01097       {
01098         pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
01099 
01100         drawLabelCandidateRect( lp, painter, xform );
01101       }
01102     }
01103   }
01104 
01105   // find the solution
01106   labels = mPal->solveProblem( problem, mShowingAllLabels );
01107 
01108   QgsDebugMsg( QString( "LABELING work:  %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ) );
01109   t.restart();
01110 
01111   painter->setRenderHint( QPainter::Antialiasing );
01112 
01113   // draw the labels
01114   std::list<LabelPosition*>::iterator it = labels->begin();
01115   for ( ; it != labels->end(); ++it )
01116   {
01117     QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
01118     if ( !palGeometry )
01119     {
01120       continue;
01121     }
01122 
01123     //layer names
01124     QString layerNameUtf8 = QString::fromUtf8(( *it )->getLayerName() );
01125     if ( palGeometry->isDiagram() )
01126     {
01127       //render diagram
01128       QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dit = mActiveDiagramLayers.begin();
01129       for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit )
01130       {
01131         if ( dit.key() && dit.key()->id().append( "d" ) == layerNameUtf8 )
01132         {
01133           QgsPoint outPt = xform->transform(( *it )->getX(), ( *it )->getY() );
01134           dit.value().renderer->renderDiagram( palGeometry->diagramAttributes(), context, QPointF( outPt.x(), outPt.y() ) );
01135         }
01136       }
01137 
01138       //insert into label search tree to manipulate position interactively
01139       if ( mLabelSearchTree )
01140       {
01141         //for diagrams, remove the additional 'd' at the end of the layer id
01142         QString layerId = layerNameUtf8;
01143         layerId.chop( 1 );
01144         mLabelSearchTree->insertLabel( *it,  QString( palGeometry->strId() ).toInt(), layerId, true );
01145       }
01146       continue;
01147     }
01148 
01149     const QgsPalLayerSettings& lyr = layer( layerNameUtf8 );
01150     QFont fontForLabel = lyr.textFont;
01151     QColor fontColor = lyr.textColor;
01152     double bufferSize = lyr.bufferSize;
01153     QColor bufferColor = lyr.bufferColor;
01154 
01155     //apply data defined settings for the label
01156     //font size
01157     QVariant dataDefinedSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Size );
01158     if ( dataDefinedSize.isValid() )
01159     {
01160       fontForLabel.setPixelSize( lyr.sizeToPixel( dataDefinedSize.toDouble(), context ) );
01161     }
01162     //font color
01163     QVariant dataDefinedColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Color );
01164     if ( dataDefinedColor.isValid() )
01165     {
01166       fontColor.setNamedColor( dataDefinedColor.toString() );
01167       if ( !fontColor.isValid() )
01168       {
01169         fontColor = lyr.textColor;
01170       }
01171     }
01172     //font bold
01173     QVariant dataDefinedBold = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Bold );
01174     if ( dataDefinedBold.isValid() )
01175     {
01176       fontForLabel.setBold(( bool )dataDefinedBold.toInt() );
01177     }
01178     //font italic
01179     QVariant dataDefinedItalic = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Italic );
01180     if ( dataDefinedItalic.isValid() )
01181     {
01182       fontForLabel.setItalic(( bool ) dataDefinedItalic.toInt() );
01183     }
01184     //font underline
01185     QVariant dataDefinedUnderline = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Underline );
01186     if ( dataDefinedUnderline.isValid() )
01187     {
01188       fontForLabel.setUnderline(( bool ) dataDefinedUnderline.toInt() );
01189     }
01190     //font strikeout
01191     QVariant dataDefinedStrikeout = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Strikeout );
01192     if ( dataDefinedStrikeout.isValid() )
01193     {
01194       fontForLabel.setStrikeOut(( bool ) dataDefinedStrikeout.toInt() );
01195     }
01196     //font family
01197     QVariant dataDefinedFontFamily = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Family );
01198     if ( dataDefinedFontFamily.isValid() )
01199     {
01200       fontForLabel.setFamily( dataDefinedFontFamily.toString() );
01201     }
01202     //buffer size
01203     QVariant dataDefinedBufferSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferSize );
01204     if ( dataDefinedBufferSize.isValid() )
01205     {
01206       bufferSize = dataDefinedBufferSize.toDouble();
01207     }
01208 
01209     //buffer color
01210     QVariant dataDefinedBufferColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferColor );
01211     if ( dataDefinedBufferColor.isValid() )
01212     {
01213       bufferColor.setNamedColor( dataDefinedBufferColor.toString() );
01214       if ( !bufferColor.isValid() )
01215       {
01216         bufferColor = lyr.bufferColor;
01217       }
01218     }
01219 
01220     if ( lyr.bufferSize != 0 )
01221       drawLabel( *it, painter, fontForLabel, fontColor, xform, bufferSize, bufferColor, true );
01222 
01223     drawLabel( *it, painter, fontForLabel, fontColor, xform );
01224 
01225     if ( mLabelSearchTree )
01226     {
01227       mLabelSearchTree->insertLabel( *it,  QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName() );
01228     }
01229   }
01230 
01231   QgsDebugMsg( QString( "LABELING draw:  %1 ms" ).arg( t.elapsed() ) );
01232 
01233   delete problem;
01234   delete labels;
01235 
01236   // delete all allocated geometries for features
01237   QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
01238   for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
01239   {
01240     QgsPalLayerSettings& lyr = lit.value();
01241     for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
01242       delete *git;
01243     lyr.geometries.clear();
01244   }
01245 
01246   //delete all allocated geometries for diagrams
01247   QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dIt = mActiveDiagramLayers.begin();
01248   for ( ; dIt != mActiveDiagramLayers.end(); ++dIt )
01249   {
01250     QgsDiagramLayerSettings& dls = dIt.value();
01251     for ( QList<QgsPalGeometry*>::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git )
01252     {
01253       delete *git;
01254     }
01255     dls.geometries.clear();
01256   }
01257 }
01258 
01259 QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
01260 {
01261   QList<QgsLabelPosition> positions;
01262 
01263   QList<QgsLabelPosition*> positionPointers;
01264   if ( mLabelSearchTree )
01265   {
01266     mLabelSearchTree->label( p, positionPointers );
01267     QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
01268     for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
01269     {
01270       positions.push_back( QgsLabelPosition( **pointerIt ) );
01271     }
01272   }
01273 
01274   return positions;
01275 }
01276 
01277 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
01278 {
01279   candPoint = mCandPoint;
01280   candLine = mCandLine;
01281   candPolygon = mCandPolygon;
01282 }
01283 
01284 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
01285 {
01286   mCandPoint = candPoint;
01287   mCandLine = candLine;
01288   mCandPolygon = candPolygon;
01289 }
01290 
01291 void QgsPalLabeling::setSearchMethod( QgsPalLabeling::Search s )
01292 {
01293   mSearch = s;
01294 }
01295 
01296 QgsPalLabeling::Search QgsPalLabeling::searchMethod() const
01297 {
01298   return mSearch;
01299 }
01300 
01301 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform )
01302 {
01303   QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
01304   QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
01305 
01306   painter->save();
01307   painter->translate( QPointF( outPt.x(), outPt.y() ) );
01308   painter->rotate( -lp->getAlpha() * 180 / M_PI );
01309   QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
01310   painter->drawRect( rect );
01311   painter->restore();
01312 
01313   // save the rect
01314   rect.moveTo( outPt.x(), outPt.y() );
01315   mCandidates.append( QgsLabelCandidate( rect, lp->getCost() * 1000 ) );
01316 
01317   // show all parts of the multipart label
01318   if ( lp->getNextPart() )
01319     drawLabelCandidateRect( lp->getNextPart(), painter, xform );
01320 }
01321 
01322 void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, const QFont& f, const QColor& c, const QgsMapToPixel* xform, double bufferSize,
01323                                 const QColor& bufferColor, bool drawBuffer )
01324 {
01325   QgsPoint outPt = xform->transform( label->getX(), label->getY() );
01326 
01327   // TODO: optimize access :)
01328   const QgsPalLayerSettings& lyr = layer( QString::fromUtf8( label->getLayerName() ) );
01329   QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
01330   QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
01331 
01332   //add the direction symbol if needed
01333   if ( !txt.isEmpty() && lyr.placement == QgsPalLayerSettings::Line &&
01334        lyr.addDirectionSymbol )
01335   {
01336     if ( label->getReversed() )
01337     {
01338       txt.prepend( "<" );
01339     }
01340     else
01341     {
01342       txt.append( ">" );
01343     }
01344   }
01345 
01346   //QgsDebugMsg( "drawLabel " + QString::number( drawBuffer ) + " " + txt );
01347 
01348   QStringList multiLineList;
01349   if ( !lyr.wrapChar.isEmpty() )
01350     multiLineList = txt.split( lyr.wrapChar );
01351   else
01352     multiLineList = txt.split( "\n" );
01353 
01354   for ( int i = 0; i < multiLineList.size(); ++i )
01355   {
01356     painter->save();
01357     painter->translate( QPointF( outPt.x(), outPt.y() ) );
01358     painter->rotate( -label->getAlpha() * 180 / M_PI );
01359 
01360     // scale down painter: the font size has been multiplied by raster scale factor
01361     // to workaround a Qt font scaling bug with small font sizes
01362     painter->scale( 1.0 / lyr.rasterCompressFactor, 1.0 / lyr.rasterCompressFactor );
01363 
01364     double yMultiLineOffset = ( multiLineList.size() - 1 - i ) * lyr.fontMetrics->height();
01365     painter->translate( QPointF( 0, - lyr.fontMetrics->descent() - yMultiLineOffset ) );
01366 
01367     if ( drawBuffer )
01368     {
01369       // we're drawing buffer
01370       drawLabelBuffer( painter, multiLineList.at( i ), f, bufferSize * lyr.vectorScaleFactor * lyr.rasterCompressFactor , bufferColor );
01371     }
01372     else
01373     {
01374       // we're drawing real label
01375       QPainterPath path;
01376       path.addText( 0, 0, f, multiLineList.at( i ) );
01377       painter->setPen( Qt::NoPen );
01378       painter->setBrush( c );
01379       painter->drawPath( path );
01380     }
01381     painter->restore();
01382 
01383     if ( label->getNextPart() )
01384       drawLabel( label->getNextPart(), painter, f, c, xform, bufferSize, bufferColor, drawBuffer );
01385   }
01386 }
01387 
01388 
01389 void QgsPalLabeling::drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color )
01390 {
01391   QPainterPath path;
01392   path.addText( 0, 0, font, text );
01393   QPen pen( color );
01394   pen.setWidthF( size );
01395   p->setPen( pen );
01396   p->setBrush( color );
01397   p->drawPath( path );
01398 }
01399 
01400 QgsLabelingEngineInterface* QgsPalLabeling::clone()
01401 {
01402   QgsPalLabeling* lbl = new QgsPalLabeling();
01403   lbl->mShowingAllLabels = mShowingAllLabels;
01404   lbl->mShowingCandidates = mShowingCandidates;
01405   return lbl;
01406 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines