Quantum GIS API Documentation
1.7.4
|
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 }