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