00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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 <qgslogger.h>
00042 #include <qgsvectorlayer.h>
00043 #include <qgsmaplayerregistry.h>
00044 #include <qgsvectordataprovider.h>
00045 #include <qgsgeometry.h>
00046 #include <qgsmaprenderer.h>
00047 #include "qgslogger.h"
00048
00049
00050 using namespace pal;
00051
00052
00053 class QgsPalGeometry : public PalGeometry
00054 {
00055 public:
00056 QgsPalGeometry( int id, QString text, GEOSGeometry* g ): mG( g ), mText( text ), mId( id ), mInfo( NULL )
00057 {
00058 mStrId = QByteArray::number( id );
00059 }
00060
00061 ~QgsPalGeometry()
00062 {
00063 if ( mG ) GEOSGeom_destroy( mG );
00064 delete mInfo;
00065 }
00066
00067
00068
00069 GEOSGeometry* getGeosGeometry()
00070 {
00071 return mG;
00072 }
00073 void releaseGeosGeometry( GEOSGeometry* )
00074 {
00075
00076 }
00077
00078 const char* strId() { return mStrId.data(); }
00079 QString text() { return mText; }
00080
00081 pal::LabelInfo* info( QFontMetricsF* fm, const QgsMapToPixel* xform, double fontScale )
00082 {
00083 if ( mInfo ) return mInfo;
00084
00085
00086 QgsPoint ptZero = xform->toMapCoordinates( 0, 0 );
00087 QgsPoint ptSize = xform->toMapCoordinatesF( 0.0, -fm->height() / fontScale );
00088
00089 mInfo = new pal::LabelInfo( mText.count(), ptSize.y() - ptZero.y() );
00090 for ( int i = 0; i < mText.count(); i++ )
00091 {
00092 mInfo->char_info[i].chr = mText[i].unicode();
00093 ptSize = xform->toMapCoordinatesF( fm->width( mText[i] ) / fontScale , 0.0 );
00094 mInfo->char_info[i].width = ptSize.x() - ptZero.x();
00095 }
00096 return mInfo;
00097 }
00098
00099 const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& dataDefinedValues() const { return mDataDefinedValues; }
00100 void addDataDefinedValue( QgsPalLayerSettings::DataDefinedProperties p, QVariant v ) { mDataDefinedValues.insert( p, v ); }
00101
00102 protected:
00103 GEOSGeometry* mG;
00104 QString mText;
00105 QByteArray mStrId;
00106 int mId;
00107 LabelInfo* mInfo;
00109 QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > mDataDefinedValues;
00110 };
00111
00112
00113
00114 QgsPalLayerSettings::QgsPalLayerSettings()
00115 : palLayer( NULL ), fontMetrics( NULL ), ct( NULL )
00116 {
00117 placement = AroundPoint;
00118 placementFlags = 0;
00119
00120 textColor = Qt::black;
00121 enabled = false;
00122 priority = 5;
00123 obstacle = true;
00124 dist = 0;
00125 scaleMin = 0;
00126 scaleMax = 0;
00127 bufferSize = 1;
00128 bufferColor = Qt::white;
00129 labelPerPart = false;
00130 mergeLines = false;
00131 multiLineLabels = false;
00132 minFeatureSize = 0.0;
00133 vectorScaleFactor = 1.0;
00134 rasterCompressFactor = 1.0;
00135 addDirectionSymbol = false;
00136 fontSizeInMapUnits = false;
00137 }
00138
00139 QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
00140 {
00141
00142 fieldName = s.fieldName;
00143 placement = s.placement;
00144 placementFlags = s.placementFlags;
00145 textFont = s.textFont;
00146 textColor = s.textColor;
00147 enabled = s.enabled;
00148 priority = s.priority;
00149 obstacle = s.obstacle;
00150 dist = s.dist;
00151 scaleMin = s.scaleMin;
00152 scaleMax = s.scaleMax;
00153 bufferSize = s.bufferSize;
00154 bufferColor = s.bufferColor;
00155 labelPerPart = s.labelPerPart;
00156 mergeLines = s.mergeLines;
00157 multiLineLabels = s.multiLineLabels;
00158 minFeatureSize = s.minFeatureSize;
00159 vectorScaleFactor = s.vectorScaleFactor;
00160 rasterCompressFactor = s.rasterCompressFactor;
00161 addDirectionSymbol = s.addDirectionSymbol;
00162 fontSizeInMapUnits = s.fontSizeInMapUnits;
00163
00164 dataDefinedProperties = s.dataDefinedProperties;
00165 fontMetrics = NULL;
00166 ct = NULL;
00167 }
00168
00169
00170 QgsPalLayerSettings::~QgsPalLayerSettings()
00171 {
00172
00173
00174 delete fontMetrics;
00175 delete ct;
00176 }
00177
00178 static QColor _readColor( QgsVectorLayer* layer, QString property )
00179 {
00180 int r = layer->customProperty( property + "R" ).toInt();
00181 int g = layer->customProperty( property + "G" ).toInt();
00182 int b = layer->customProperty( property + "B" ).toInt();
00183 return QColor( r, g, b );
00184 }
00185
00186 static void _writeColor( QgsVectorLayer* layer, QString property, QColor color )
00187 {
00188 layer->setCustomProperty( property + "R", color.red() );
00189 layer->setCustomProperty( property + "G", color.green() );
00190 layer->setCustomProperty( property + "B", color.blue() );
00191 }
00192
00193 static void _writeDataDefinedPropertyMap( QgsVectorLayer* layer, const QMap< QgsPalLayerSettings::DataDefinedProperties, int >& propertyMap )
00194 {
00195 if ( !layer )
00196 {
00197 return;
00198 }
00199
00200 for ( int i = 0; i < 15; ++i )
00201 {
00202 QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator it = propertyMap.find(( QgsPalLayerSettings::DataDefinedProperties )i );
00203 QVariant propertyValue;
00204 if ( it == propertyMap.constEnd() )
00205 {
00206 propertyValue = QVariant();
00207 }
00208 else
00209 {
00210 propertyValue = *it;
00211 }
00212 layer->setCustomProperty( "labeling/dataDefinedProperty" + QString::number( i ), propertyValue );
00213 }
00214 }
00215
00216 static void _readDataDefinedProperty( QgsVectorLayer* layer, QgsPalLayerSettings::DataDefinedProperties p, \
00217 QMap< QgsPalLayerSettings::DataDefinedProperties, int >& propertyMap )
00218 {
00219 QVariant propertyField = layer->customProperty( "labeling/dataDefinedProperty" + QString::number( p ) );
00220 bool conversionOk;
00221 int fieldIndex;
00222
00223 if ( propertyField.isValid() )
00224 {
00225 fieldIndex = propertyField.toInt( &conversionOk );
00226 if ( conversionOk )
00227 {
00228 propertyMap.insert( p, fieldIndex );
00229 }
00230 }
00231 }
00232
00233 static void _readDataDefinedPropertyMap( QgsVectorLayer* layer, QMap< QgsPalLayerSettings::DataDefinedProperties, int >& propertyMap )
00234 {
00235 if ( !layer )
00236 {
00237 return;
00238 }
00239
00240 _readDataDefinedProperty( layer, QgsPalLayerSettings::Size, propertyMap );
00241 _readDataDefinedProperty( layer, QgsPalLayerSettings::Color, propertyMap );
00242 _readDataDefinedProperty( layer, QgsPalLayerSettings::Bold, propertyMap );
00243 _readDataDefinedProperty( layer, QgsPalLayerSettings::Italic, propertyMap );
00244 _readDataDefinedProperty( layer, QgsPalLayerSettings::Underline, propertyMap );
00245 _readDataDefinedProperty( layer, QgsPalLayerSettings::Strikeout, propertyMap );
00246 _readDataDefinedProperty( layer, QgsPalLayerSettings::Family, propertyMap );
00247 _readDataDefinedProperty( layer, QgsPalLayerSettings::BufferSize, propertyMap );
00248 _readDataDefinedProperty( layer, QgsPalLayerSettings::BufferColor, propertyMap );
00249 _readDataDefinedProperty( layer, QgsPalLayerSettings::PositionX, propertyMap );
00250 _readDataDefinedProperty( layer, QgsPalLayerSettings::PositionY, propertyMap );
00251 _readDataDefinedProperty( layer, QgsPalLayerSettings::Hali, propertyMap );
00252 _readDataDefinedProperty( layer, QgsPalLayerSettings::Vali, propertyMap );
00253 _readDataDefinedProperty( layer, QgsPalLayerSettings::LabelDistance, propertyMap );
00254 _readDataDefinedProperty( layer, QgsPalLayerSettings::Rotation, propertyMap );
00255 }
00256
00257 void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
00258 {
00259 if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) )
00260 return;
00261
00262 fieldName = layer->customProperty( "labeling/fieldName" ).toString();
00263 placement = ( Placement ) layer->customProperty( "labeling/placement" ).toInt();
00264 placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
00265 QString fontFamily = layer->customProperty( "labeling/fontFamily" ).toString();
00266 double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
00267 int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
00268 bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
00269 textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
00270 textFont.setPointSizeF( fontSize );
00271 textColor = _readColor( layer, "labeling/textColor" );
00272 enabled = layer->customProperty( "labeling/enabled" ).toBool();
00273 priority = layer->customProperty( "labeling/priority" ).toInt();
00274 obstacle = layer->customProperty( "labeling/obstacle" ).toBool();
00275 dist = layer->customProperty( "labeling/dist" ).toDouble();
00276 scaleMin = layer->customProperty( "labeling/scaleMin" ).toInt();
00277 scaleMax = layer->customProperty( "labeling/scaleMax" ).toInt();
00278 bufferSize = layer->customProperty( "labeling/bufferSize" ).toDouble();
00279 bufferColor = _readColor( layer, "labeling/bufferColor" );
00280 labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
00281 mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
00282 multiLineLabels = layer->customProperty( "labeling/multiLineLabels" ).toBool();
00283 addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
00284 minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
00285 fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
00286 _readDataDefinedPropertyMap( layer, dataDefinedProperties );
00287 }
00288
00289 void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
00290 {
00291
00292 layer->setCustomProperty( "labeling", "pal" );
00293
00294 layer->setCustomProperty( "labeling/fieldName", fieldName );
00295 layer->setCustomProperty( "labeling/placement", placement );
00296 layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
00297
00298 layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
00299 layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
00300 layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
00301 layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
00302
00303 _writeColor( layer, "labeling/textColor", textColor );
00304 layer->setCustomProperty( "labeling/enabled", enabled );
00305 layer->setCustomProperty( "labeling/priority", priority );
00306 layer->setCustomProperty( "labeling/obstacle", obstacle );
00307 layer->setCustomProperty( "labeling/dist", dist );
00308 layer->setCustomProperty( "labeling/scaleMin", scaleMin );
00309 layer->setCustomProperty( "labeling/scaleMax", scaleMax );
00310 layer->setCustomProperty( "labeling/bufferSize", bufferSize );
00311 _writeColor( layer, "labeling/bufferColor", bufferColor );
00312 layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
00313 layer->setCustomProperty( "labeling/mergeLines", mergeLines );
00314 layer->setCustomProperty( "labeling/multiLineLabels", multiLineLabels );
00315 layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
00316 layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
00317 layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
00318 _writeDataDefinedPropertyMap( layer, dataDefinedProperties );
00319 }
00320
00321 void QgsPalLayerSettings::setDataDefinedProperty( DataDefinedProperties p, int attributeIndex )
00322 {
00323 dataDefinedProperties.insert( p, attributeIndex );
00324 }
00325
00326 void QgsPalLayerSettings::removeDataDefinedProperty( DataDefinedProperties p )
00327 {
00328 dataDefinedProperties.remove( p );
00329 }
00330
00331 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, QgsGeometry* geom, double minSize ) const
00332 {
00333 if ( minSize <= 0 )
00334 {
00335 return true;
00336 }
00337
00338 if ( !geom )
00339 {
00340 return false;
00341 }
00342
00343 QGis::GeometryType featureType = geom->type();
00344 if ( featureType == QGis::Point )
00345 {
00346 return true;
00347 }
00348
00349 double mapUnitsPerMM = ct.mapToPixel().mapUnitsPerPixel() * ct.scaleFactor();
00350 if ( featureType == QGis::Line )
00351 {
00352 double length = geom->length();
00353 if ( length >= 0.0 )
00354 {
00355 return ( length >= ( minSize * mapUnitsPerMM ) );
00356 }
00357 }
00358 else if ( featureType == QGis::Polygon )
00359 {
00360 double area = geom->area();
00361 if ( area >= 0.0 )
00362 {
00363 return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
00364 }
00365 }
00366 return true;
00367 }
00368
00369 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY )
00370 {
00371 if ( !fm )
00372 {
00373 return;
00374 }
00375
00376 if ( addDirectionSymbol && !multiLineLabels && placement == QgsPalLayerSettings::Line )
00377 {
00378 text.append( ">" );
00379 }
00380 QRectF labelRect = fm->boundingRect( text );
00381 double w, h;
00382 if ( !multiLineLabels )
00383 {
00384 w = labelRect.width() / rasterCompressFactor;
00385 h = labelRect.height() / rasterCompressFactor;
00386 }
00387 else
00388 {
00389 QStringList multiLineSplit = text.split( "\n" );
00390 h = fm->height() * multiLineSplit.size() / rasterCompressFactor;
00391 w = 0;
00392 for ( int i = 0; i < multiLineSplit.size(); ++i )
00393 {
00394 double width = fm->width( multiLineSplit.at( i ) );
00395 if ( width > w )
00396 {
00397 w = width;
00398 }
00399 w /= rasterCompressFactor;
00400 }
00401 }
00402 QgsPoint ptSize = xform->toMapCoordinates( w, h );
00403
00404 labelX = fabs( ptSize.x() - ptZero.x() );
00405 labelY = fabs( ptSize.y() - ptZero.y() );
00406 }
00407
00408
00409 void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context )
00410 {
00411 QString labelText = f.attributeMap()[fieldIndex].toString();
00412 double labelX, labelY;
00413 QFont labelFont = textFont;
00414
00415
00416 QMap< DataDefinedProperties, int >::const_iterator it = dataDefinedProperties.find( QgsPalLayerSettings::Size );
00417 if ( it != dataDefinedProperties.constEnd() )
00418 {
00419
00420 QVariant size = f.attributeMap().value( *it );
00421 if ( size.isValid() )
00422 {
00423 double sizeDouble = size.toDouble();
00424 if ( sizeDouble <= 0 )
00425 {
00426 return;
00427 }
00428 labelFont.setPixelSize( sizeToPixel( sizeDouble, context ) );
00429 }
00430 QFontMetricsF labelFontMetrics( labelFont );
00431 calculateLabelSize( &labelFontMetrics, labelText, labelX, labelY );
00432 }
00433 else
00434 {
00435 calculateLabelSize( fontMetrics, labelText, labelX, labelY );
00436 }
00437
00438 QgsGeometry* geom = f.geometry();
00439
00440 if ( ct )
00441 geom->transform( *ct );
00442
00443 GEOSGeometry* geos_geom = geom->asGeos();
00444 if ( geos_geom == NULL )
00445 return;
00446
00447 if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) )
00448 {
00449 return;
00450 }
00451
00452
00453 bool dataDefinedPosition = false;
00454 bool dataDefinedRotation = false;
00455 double xPos, yPos, angle;
00456
00457 QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX );
00458 if ( dPosXIt != dataDefinedProperties.constEnd() )
00459 {
00460 QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY );
00461 if ( dPosYIt != dataDefinedProperties.constEnd() )
00462 {
00463
00464 dataDefinedPosition = true;
00465 xPos = f.attributeMap().value( *dPosXIt ).toDouble();
00466 yPos = f.attributeMap().value( *dPosYIt ).toDouble();
00467
00468
00469 double xdiff = 0;
00470 double ydiff = 0;
00471
00472
00473 QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali );
00474 if ( haliIt != dataDefinedProperties.end() )
00475 {
00476 QString haliString = f.attributeMap().value( *haliIt ).toString();
00477 if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
00478 {
00479 xdiff -= labelX / 2.0;
00480 }
00481 else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
00482 {
00483 xdiff -= labelX;
00484 }
00485 }
00486
00487
00488 QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali );
00489 if ( valiIt != dataDefinedProperties.constEnd() )
00490 {
00491 QString valiString = f.attributeMap().value( *valiIt ).toString();
00492 if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
00493 {
00494 if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
00495 {
00496 ydiff -= labelY;
00497 }
00498 else
00499 {
00500 QFontMetrics labelFontMetrics( labelFont );
00501 double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
00502
00503 if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
00504 {
00505 ydiff -= labelY * descentRatio;
00506 }
00507 else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
00508 {
00509 ydiff -= labelY * descentRatio;
00510 ydiff -= labelY * 0.5 * ( 1 - descentRatio );
00511 }
00512 }
00513 }
00514 }
00515
00516
00517 QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation );
00518 if ( rotIt != dataDefinedProperties.constEnd() )
00519 {
00520 dataDefinedRotation = true;
00521 angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180;
00522
00523 double xd = xdiff * cos( angle ) - ydiff * sin( angle );
00524 double yd = xdiff * sin( angle ) + ydiff * cos( angle );
00525 xdiff = xd;
00526 ydiff = yd;
00527 }
00528
00529 yPos += ydiff;
00530 xPos += xdiff;
00531 }
00532 }
00533
00534 QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), labelText, GEOSGeom_clone( geos_geom ) );
00535
00536
00537 geometries.append( lbl );
00538
00539
00540 try
00541 {
00542 if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
00543 xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation ) )
00544 return;
00545 }
00546 catch ( std::exception* e )
00547 {
00548 QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( f.id() ) + QString::fromLatin1( e->what() ) );
00549 return;
00550 }
00551
00552
00553 pal::Feature* feat = palLayer->getFeature( lbl->strId() );
00554 feat->setLabelInfo( lbl->info( fontMetrics, xform, rasterCompressFactor ) );
00555
00556
00557
00558
00559 double distance = dist;
00560 QMap< DataDefinedProperties, int >::const_iterator dDistIt = dataDefinedProperties.find( QgsPalLayerSettings::LabelDistance );
00561 if ( dDistIt != dataDefinedProperties.constEnd() )
00562 {
00563 distance = f.attributeMap().value( *dDistIt ).toDouble();
00564 }
00565
00566 if ( distance != 0 )
00567 {
00568 feat->setDistLabel( fabs( ptOne.x() - ptZero.x() )* distance * vectorScaleFactor );
00569 }
00570
00571
00572 QMap< DataDefinedProperties, int >::const_iterator dIt = dataDefinedProperties.constBegin();
00573 for ( ; dIt != dataDefinedProperties.constEnd(); ++dIt )
00574 {
00575 lbl->addDataDefinedValue( dIt.key(), f.attributeMap()[dIt.value()] );
00576 }
00577 }
00578
00579 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c ) const
00580 {
00581 double pixelSize;
00582 if ( fontSizeInMapUnits )
00583 {
00584 pixelSize = size / c.mapToPixel().mapUnitsPerPixel() * c.rasterScaleFactor();
00585 }
00586 else
00587 {
00588
00589 pixelSize = 0.3527 * size * c.scaleFactor() * c.rasterScaleFactor();
00590 }
00591 return ( int )( pixelSize + 0.5 );
00592 }
00593
00594
00595
00596
00597 QgsPalLabeling::QgsPalLabeling()
00598 : mMapRenderer( NULL ), mPal( NULL )
00599 {
00600
00601
00602 Pal p;
00603 mCandPoint = p.getPointP();
00604 mCandLine = p.getLineP();
00605 mCandPolygon = p.getPolyP();
00606
00607 switch ( p.getSearch() )
00608 {
00609 case CHAIN: mSearch = Chain; break;
00610 case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
00611 case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
00612 case POPMUSIC_TABU_CHAIN: mSearch = Popmusic_Tabu_Chain; break;
00613 case FALP: mSearch = Falp; break;
00614 }
00615
00616 mShowingCandidates = false;
00617 mShowingAllLabels = false;
00618 }
00619
00620
00621 QgsPalLabeling::~QgsPalLabeling()
00622 {
00623
00624 exit();
00625 }
00626
00627
00628 bool QgsPalLabeling::willUseLayer( QgsVectorLayer* layer )
00629 {
00630 QgsPalLayerSettings lyrTmp;
00631 lyrTmp.readFromLayer( layer );
00632 return lyrTmp.enabled;
00633 }
00634
00635 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx )
00636 {
00637 Q_ASSERT( mMapRenderer != NULL );
00638
00639
00640 QgsPalLayerSettings lyrTmp;
00641 lyrTmp.readFromLayer( layer );
00642
00643 if ( !lyrTmp.enabled )
00644 return 0;
00645
00646
00647 int fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
00648 if ( fldIndex == -1 )
00649 return 0;
00650 attrIndices.insert( fldIndex );
00651
00652
00653 QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator dIt = lyrTmp.dataDefinedProperties.constBegin();
00654 for ( ; dIt != lyrTmp.dataDefinedProperties.constEnd(); ++dIt )
00655 {
00656 attrIndices.insert( dIt.value() );
00657 }
00658
00659
00660 mActiveLayers.insert( layer, lyrTmp );
00661
00662 QgsPalLayerSettings& lyr = mActiveLayers[layer];
00663
00664
00665 Arrangement arrangement;
00666 switch ( lyr.placement )
00667 {
00668 case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
00669 case QgsPalLayerSettings::OverPoint: arrangement = P_POINT_OVER; break;
00670 case QgsPalLayerSettings::Line: arrangement = P_LINE; break;
00671 case QgsPalLayerSettings::Curved: arrangement = P_CURVED; break;
00672 case QgsPalLayerSettings::Horizontal: arrangement = P_HORIZ; break;
00673 case QgsPalLayerSettings::Free: arrangement = P_FREE; break;
00674 default: Q_ASSERT( "unsupported placement" && 0 ); return 0; break;
00675 }
00676
00677
00678 double priority = 1 - lyr.priority / 10.0;
00679 double min_scale = -1, max_scale = -1;
00680 if ( lyr.scaleMin != 0 && lyr.scaleMax != 0 )
00681 {
00682 min_scale = lyr.scaleMin;
00683 max_scale = lyr.scaleMax;
00684 }
00685
00686 Layer* l = mPal->addLayer( layer->getLayerID().toLocal8Bit().data(),
00687 min_scale, max_scale, arrangement,
00688 METER, priority, lyr.obstacle, true, true );
00689
00690 if ( lyr.placementFlags )
00691 l->setArrangementFlags( lyr.placementFlags );
00692
00693
00694 l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
00695
00696
00697 l->setMergeConnectedLines( lyr.mergeLines );
00698
00699 lyr.textFont.setPixelSize( lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx ) );
00700
00701
00702 lyr.vectorScaleFactor = ctx.scaleFactor();
00703 lyr.rasterCompressFactor = ctx.rasterScaleFactor();
00704
00705
00706 lyr.palLayer = l;
00707 lyr.fieldIndex = fldIndex;
00708 lyr.fontMetrics = new QFontMetricsF( lyr.textFont );
00709
00710 lyr.xform = mMapRenderer->coordinateTransform();
00711 if ( mMapRenderer->hasCrsTransformEnabled() )
00712 lyr.ct = new QgsCoordinateTransform( layer->srs(), mMapRenderer->destinationSrs() );
00713 else
00714 lyr.ct = NULL;
00715 lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
00716 lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
00717
00718 return 1;
00719 }
00720
00721
00722 void QgsPalLabeling::registerFeature( QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
00723 {
00724 QgsPalLayerSettings& lyr = mActiveLayers[layer];
00725 lyr.registerFeature( f, context );
00726 }
00727
00728
00729 void QgsPalLabeling::init( QgsMapRenderer* mr )
00730 {
00731 mMapRenderer = mr;
00732
00733
00734 if ( mPal )
00735 delete mPal;
00736
00737 mPal = new Pal;
00738
00739 SearchMethod s;
00740 switch ( mSearch )
00741 {
00742 default:
00743 case Chain: s = CHAIN; break;
00744 case Popmusic_Tabu: s = POPMUSIC_TABU; break;
00745 case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
00746 case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
00747 case Falp: s = FALP; break;
00748 }
00749 mPal->setSearch( s );
00750
00751
00752 mPal->setPointP( mCandPoint );
00753 mPal->setLineP( mCandLine );
00754 mPal->setPolyP( mCandPolygon );
00755 }
00756
00757 void QgsPalLabeling::exit()
00758 {
00759 delete mPal;
00760 mPal = NULL;
00761 mMapRenderer = NULL;
00762 }
00763
00764 QgsPalLayerSettings& QgsPalLabeling::layer( const char* layerName )
00765 {
00766 QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
00767 for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
00768 {
00769 QgsPalLayerSettings& lyr = lit.value();
00770 if ( lyr.palLayer->getName() == layerName )
00771 return lyr;
00772 }
00773 return mInvalidLayerSettings;
00774 }
00775
00776
00777 void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
00778 {
00779 Q_ASSERT( mMapRenderer != NULL );
00780 QPainter* painter = context.painter();
00781 QgsRectangle extent = context.extent();
00782
00783 QTime t;
00784 t.start();
00785
00786
00787 double scale = mMapRenderer->scale();
00788 QgsRectangle r = extent;
00789 double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() };
00790
00791 std::list<LabelPosition*>* labels;
00792 pal::Problem* problem;
00793 try
00794 {
00795 problem = mPal->extractProblem( scale, bbox );
00796 }
00797 catch ( std::exception& e )
00798 {
00799 QgsDebugMsg( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ) );
00800 mActiveLayers.clear();
00801 return;
00802 }
00803
00804 const QgsMapToPixel* xform = mMapRenderer->coordinateTransform();
00805
00806
00807
00808
00809 mCandidates.clear();
00810 if ( mShowingCandidates && problem )
00811 {
00812 painter->setPen( QColor( 0, 0, 0, 64 ) );
00813 painter->setBrush( Qt::NoBrush );
00814 for ( int i = 0; i < problem->getNumFeatures(); i++ )
00815 {
00816 for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
00817 {
00818 pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
00819
00820 drawLabelCandidateRect( lp, painter, xform );
00821 }
00822 }
00823 }
00824
00825
00826 labels = mPal->solveProblem( problem, mShowingAllLabels );
00827
00828 QgsDebugMsg( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ) );
00829 t.restart();
00830
00831 painter->setRenderHint( QPainter::Antialiasing );
00832
00833
00834 std::list<LabelPosition*>::iterator it = labels->begin();
00835 for ( ; it != labels->end(); ++it )
00836 {
00837 const QgsPalLayerSettings& lyr = layer(( *it )->getLayerName() );
00838 QFont fontForLabel = lyr.textFont;
00839 QColor fontColor = lyr.textColor;
00840 double bufferSize = lyr.bufferSize;
00841 QColor bufferColor = lyr.bufferColor;
00842
00843 QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
00844 if ( !palGeometry )
00845 {
00846 continue;
00847 }
00848
00849
00850
00851 QVariant dataDefinedSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Size );
00852 if ( dataDefinedSize.isValid() )
00853 {
00854 fontForLabel.setPixelSize( lyr.sizeToPixel( dataDefinedSize.toDouble(), context ) );
00855 }
00856
00857 QVariant dataDefinedColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Color );
00858 if ( dataDefinedColor.isValid() )
00859 {
00860 fontColor.setNamedColor( dataDefinedColor.toString() );
00861 if ( !fontColor.isValid() )
00862 {
00863 fontColor = lyr.textColor;
00864 }
00865 }
00866
00867 QVariant dataDefinedBold = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Bold );
00868 if ( dataDefinedBold.isValid() )
00869 {
00870 fontForLabel.setBold(( bool )dataDefinedBold.toInt() );
00871 }
00872
00873 QVariant dataDefinedItalic = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Italic );
00874 if ( dataDefinedItalic.isValid() )
00875 {
00876 fontForLabel.setItalic(( bool ) dataDefinedItalic.toInt() );
00877 }
00878
00879 QVariant dataDefinedUnderline = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Underline );
00880 if ( dataDefinedUnderline.isValid() )
00881 {
00882 fontForLabel.setUnderline(( bool ) dataDefinedUnderline.toInt() );
00883 }
00884
00885 QVariant dataDefinedStrikeout = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Strikeout );
00886 if ( dataDefinedStrikeout.isValid() )
00887 {
00888 fontForLabel.setStrikeOut(( bool ) dataDefinedStrikeout.toInt() );
00889 }
00890
00891 QVariant dataDefinedFontFamily = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Family );
00892 if ( dataDefinedFontFamily.isValid() )
00893 {
00894 fontForLabel.setFamily( dataDefinedFontFamily.toString() );
00895 }
00896
00897 QVariant dataDefinedBufferSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferSize );
00898 if ( dataDefinedBufferSize.isValid() )
00899 {
00900 bufferSize = dataDefinedBufferSize.toDouble();
00901 }
00902
00903
00904 QVariant dataDefinedBufferColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferColor );
00905 if ( dataDefinedBufferColor.isValid() )
00906 {
00907 bufferColor.setNamedColor( dataDefinedBufferColor.toString() );
00908 if ( !bufferColor.isValid() )
00909 {
00910 bufferColor = lyr.bufferColor;
00911 }
00912 }
00913
00914 if ( lyr.bufferSize != 0 )
00915 drawLabel( *it, painter, fontForLabel, fontColor, xform, bufferSize, bufferColor, true );
00916
00917 drawLabel( *it, painter, fontForLabel, fontColor, xform );
00918 }
00919
00920 QgsDebugMsg( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ) );
00921
00922 delete problem;
00923 delete labels;
00924
00925
00926 QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
00927 for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
00928 {
00929 QgsPalLayerSettings& lyr = lit.value();
00930 for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
00931 delete *git;
00932 lyr.geometries.clear();
00933 }
00934
00935 mActiveLayers.clear();
00936
00937 }
00938
00939 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
00940 {
00941 candPoint = mCandPoint;
00942 candLine = mCandLine;
00943 candPolygon = mCandPolygon;
00944 }
00945
00946 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
00947 {
00948 mCandPoint = candPoint;
00949 mCandLine = candLine;
00950 mCandPolygon = candPolygon;
00951 }
00952
00953 void QgsPalLabeling::setSearchMethod( QgsPalLabeling::Search s )
00954 {
00955 mSearch = s;
00956 }
00957
00958 QgsPalLabeling::Search QgsPalLabeling::searchMethod() const
00959 {
00960 return mSearch;
00961 }
00962
00963 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform )
00964 {
00965 QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
00966 QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
00967
00968 painter->save();
00969 painter->translate( QPointF( outPt.x(), outPt.y() ) );
00970 painter->rotate( -lp->getAlpha() * 180 / M_PI );
00971 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
00972 painter->drawRect( rect );
00973 painter->restore();
00974
00975
00976 rect.moveTo( outPt.x(), outPt.y() );
00977 mCandidates.append( QgsLabelCandidate( rect, lp->getCost() * 1000 ) );
00978
00979
00980 if ( lp->getNextPart() )
00981 drawLabelCandidateRect( lp->getNextPart(), painter, xform );
00982 }
00983
00984 void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, const QFont& f, const QColor& c, const QgsMapToPixel* xform, double bufferSize,
00985 const QColor& bufferColor, bool drawBuffer )
00986 {
00987 QgsPoint outPt = xform->transform( label->getX(), label->getY() );
00988
00989
00990 const QgsPalLayerSettings& lyr = layer( label->getLayerName() );
00991
00992 QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
00993 QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
00994
00995
00996 if ( !txt.isEmpty() && lyr.placement == QgsPalLayerSettings::Line &&
00997 lyr.addDirectionSymbol && !lyr.multiLineLabels )
00998 {
00999 if ( label->getReversed() )
01000 {
01001 txt.prepend( "<" );
01002 }
01003 else
01004 {
01005 txt.append( ">" );
01006 }
01007 }
01008
01009
01010
01011 QStringList multiLineList;
01012 if ( lyr.multiLineLabels )
01013 {
01014 multiLineList = txt.split( "\n" );
01015 }
01016 else
01017 {
01018 multiLineList << txt;
01019 }
01020
01021 for ( int i = 0; i < multiLineList.size(); ++i )
01022 {
01023 painter->save();
01024 painter->translate( QPointF( outPt.x(), outPt.y() ) );
01025 painter->rotate( -label->getAlpha() * 180 / M_PI );
01026
01027
01028
01029 painter->scale( 1.0 / lyr.rasterCompressFactor, 1.0 / lyr.rasterCompressFactor );
01030
01031 double yMultiLineOffset = ( multiLineList.size() - 1 - i ) * lyr.fontMetrics->height();
01032 painter->translate( QPointF( 0, - lyr.fontMetrics->descent() - yMultiLineOffset ) );
01033
01034 if ( drawBuffer )
01035 {
01036
01037 drawLabelBuffer( painter, multiLineList.at( i ), f, bufferSize * lyr.vectorScaleFactor * lyr.rasterCompressFactor , bufferColor );
01038 }
01039 else
01040 {
01041
01042 QPainterPath path;
01043 path.addText( 0, 0, f, multiLineList.at( i ) );
01044 painter->setPen( Qt::NoPen );
01045 painter->setBrush( c );
01046 painter->drawPath( path );
01047 }
01048 painter->restore();
01049
01050 if ( label->getNextPart() )
01051 drawLabel( label->getNextPart(), painter, f, c, xform, bufferSize, bufferColor, drawBuffer );
01052 }
01053 }
01054
01055
01056 void QgsPalLabeling::drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color )
01057 {
01058
01059
01060
01061
01062
01063
01064
01065
01066 QPainterPath path;
01067 path.addText( 0, 0, font, text );
01068 QPen pen( color );
01069 pen.setWidthF( size );
01070 p->setPen( pen );
01071 p->setBrush( color );
01072 p->drawPath( path );
01073 }
01074
01075 QgsLabelingEngineInterface* QgsPalLabeling::clone()
01076 {
01077 QgsPalLabeling* lbl = new QgsPalLabeling();
01078 lbl->mShowingAllLabels = mShowingAllLabels;
01079 lbl->mShowingCandidates = mShowingCandidates;
01080 return lbl;
01081 }