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