From d0fcc9557f7609dc7d8d0f409219138bc000ebf1 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Fri, 18 Sep 2015 20:24:43 +0800 Subject: [PATCH] More labeling engine refactoring - QgsPalLabeling now internally uses new engine - label/diagram providers can hook into rendering loop to avoid extra feature loops - map rendering uses the new engine instead of QgsPalLabeling This code has been funded by Tuscany Region (Italy) - SITA (CIG: 63526840AE) and commissioned to Gis3W s.a.s. --- python/core/qgsmaprenderer.sip | 9 +- python/core/qgspallabeling.sip | 3 +- src/core/qgslabelingenginev2.cpp | 51 +- src/core/qgslabelingenginev2.h | 19 +- src/core/qgsmaprenderer.h | 10 +- src/core/qgsmaprenderercustompainterjob.cpp | 4 +- src/core/qgsmaprendererparalleljob.cpp | 4 +- src/core/qgspallabeling.cpp | 1164 ++----------------- src/core/qgspallabeling.h | 64 +- src/core/qgsvectorlayerdiagramprovider.cpp | 161 +-- src/core/qgsvectorlayerdiagramprovider.h | 13 +- src/core/qgsvectorlayerlabelprovider.cpp | 112 +- src/core/qgsvectorlayerlabelprovider.h | 25 +- src/core/qgsvectorlayerrenderer.cpp | 92 +- src/core/qgsvectorlayerrenderer.h | 11 + tests/src/core/testqgslabelingenginev2.cpp | 18 +- 16 files changed, 503 insertions(+), 1257 deletions(-) diff --git a/python/core/qgsmaprenderer.sip b/python/core/qgsmaprenderer.sip index e9395bbd87ef..a6ea2444eb35 100644 --- a/python/core/qgsmaprenderer.sip +++ b/python/core/qgsmaprenderer.sip @@ -47,9 +47,14 @@ class QgsLabelingEngineInterface //! called when starting rendering of a layer virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0; //! returns PAL layer settings for a registered layer - virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0; + //! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer() + virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0 /Deprecated/; //! adds a diagram layer to the labeling engine - virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ); + //! @note added in QGIS 2.12 + virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ); + //! adds a diagram layer to the labeling engine + //! @deprecated since 2.12 - use prepareDiagramLayer() + virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ) /Deprecated/; //! called for every feature virtual void registerFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context = QgsRenderContext(), QString dxfLayer = QString::null ) = 0; //! called for every diagram feature diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip index 0c6c51c91101..56323342fc30 100644 --- a/python/core/qgspallabeling.sip +++ b/python/core/qgspallabeling.sip @@ -699,7 +699,8 @@ class QgsPalLabeling : QgsLabelingEngineInterface bool isShowingCandidates() const; void setShowingCandidates( bool showing ); - const QList& candidates(); + //! @deprecated since 2.12 + const QList& candidates() /Deprecated/; bool isShowingShadowRectangles() const; void setShowingShadowRectangles( bool showing ); diff --git a/src/core/qgslabelingenginev2.cpp b/src/core/qgslabelingenginev2.cpp index b1249705606d..c8055798771a 100644 --- a/src/core/qgslabelingenginev2.cpp +++ b/src/core/qgslabelingenginev2.cpp @@ -17,6 +17,7 @@ #include "qgslogger.h" #include "qgspalgeometry.h" +#include "qgsproject.h" #include "feature.h" #include "labelposition.h" @@ -33,9 +34,8 @@ static bool _palIsCancelled( void* ctx ) } -QgsLabelingEngineV2::QgsLabelingEngineV2( const QgsMapSettings& mapSettings ) - : mMapSettings( mapSettings ) - , mFlags( RenderOutlineLabels | UsePartialCandidates ) +QgsLabelingEngineV2::QgsLabelingEngineV2() + : mFlags( RenderOutlineLabels | UsePartialCandidates ) , mSearchMethod( QgsPalLabeling::Chain ) , mCandPoint( 8 ) , mCandLine( 8 ) @@ -57,6 +57,15 @@ void QgsLabelingEngineV2::addProvider( QgsAbstractLabelProvider* provider ) mProviders << provider; } +void QgsLabelingEngineV2::removeProvider( QgsAbstractLabelProvider* provider ) +{ + int idx = mProviders.indexOf( provider ); + if ( idx >= 0 ) + { + delete mProviders.takeAt( idx ); + } +} + void QgsLabelingEngineV2::run( QgsRenderContext& context ) { pal::Pal p; @@ -146,7 +155,7 @@ void QgsLabelingEngineV2::run( QgsRenderContext& context ) l->setUpsidedownLabels( upsdnlabels ); - QList features = provider->labelFeatures( mMapSettings, context ); + QList features = provider->labelFeatures( context ); foreach ( QgsLabelFeature* feature, features ) { @@ -286,6 +295,40 @@ QgsLabelingResults* QgsLabelingEngineV2::takeResults() return res; } + +void QgsLabelingEngineV2::readSettingsFromProject() +{ + bool saved = false; + QgsProject* prj = QgsProject::instance(); + mSearchMethod = ( QgsPalLabeling::Search )( prj->readNumEntry( "PAL", "/SearchMethod", ( int ) QgsPalLabeling::Chain, &saved ) ); + mCandPoint = prj->readNumEntry( "PAL", "/CandidatesPoint", 8, &saved ); + mCandLine = prj->readNumEntry( "PAL", "/CandidatesLine", 8, &saved ); + mCandPolygon = prj->readNumEntry( "PAL", "/CandidatesPolygon", 8, &saved ); + + mFlags = 0; + if ( prj->readBoolEntry( "PAL", "/ShowingCandidates", false, &saved ) ) mFlags |= DrawCandidates; + if ( prj->readBoolEntry( "PAL", "/DrawRectOnly", false, &saved ) ) mFlags |= DrawLabelRectOnly; + if ( prj->readBoolEntry( "PAL", "/ShowingShadowRects", false, &saved ) ) mFlags |= DrawShadowRects; + if ( prj->readBoolEntry( "PAL", "/ShowingAllLabels", false, &saved ) ) mFlags |= UseAllLabels; + if ( prj->readBoolEntry( "PAL", "/ShowingPartialsLabels", true, &saved ) ) mFlags |= UsePartialCandidates; + if ( prj->readBoolEntry( "PAL", "/DrawOutlineLabels", true, &saved ) ) mFlags |= RenderOutlineLabels; +} + +void QgsLabelingEngineV2::writeSettingsToProject() +{ + QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearchMethod ); + QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint ); + QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine ); + QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon ); + + QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mFlags.testFlag( DrawCandidates ) ); + QgsProject::instance()->writeEntry( "PAL", "/DrawRectOnly", mFlags.testFlag( DrawLabelRectOnly ) ); + QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mFlags.testFlag( DrawShadowRects ) ); + QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mFlags.testFlag( UseAllLabels ) ); + QgsProject::instance()->writeEntry( "PAL", "/ShowingPartialsLabels", mFlags.testFlag( UsePartialCandidates ) ); + QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", mFlags.testFlag( RenderOutlineLabels ) ); +} + QgsAbstractLabelProvider* QgsLabelingEngineV2::providerById( const QString& id ) { Q_FOREACH ( QgsAbstractLabelProvider* provider, mProviders ) diff --git a/src/core/qgslabelingenginev2.h b/src/core/qgslabelingenginev2.h index 5f11de9c42a3..ded29cfa1f10 100644 --- a/src/core/qgslabelingenginev2.h +++ b/src/core/qgslabelingenginev2.h @@ -164,7 +164,7 @@ class CORE_EXPORT QgsAbstractLabelProvider virtual QString id() const = 0; //! Return list of labels - virtual QList labelFeatures( const QgsMapSettings& mapSettings, const QgsRenderContext& context ) = 0; + virtual QList labelFeatures( const QgsRenderContext& context ) = 0; //! draw this label at the position determined by the labeling engine virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const = 0; @@ -210,7 +210,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( QgsAbstractLabelProvider::Flags ) class CORE_EXPORT QgsLabelingEngineV2 { public: - QgsLabelingEngineV2( const QgsMapSettings& mapSettings ); + QgsLabelingEngineV2(); ~QgsLabelingEngineV2(); enum Flag @@ -224,9 +224,18 @@ class CORE_EXPORT QgsLabelingEngineV2 }; Q_DECLARE_FLAGS( Flags, Flag ) + void setMapSettings( const QgsMapSettings& mapSettings ) { mMapSettings = mapSettings; } + const QgsMapSettings& mapSettings() const { return mMapSettings; } + //! Add provider of label features. Takes ownership of the provider void addProvider( QgsAbstractLabelProvider* provider ); + //! Remove provider if the provider's initialization failed. Provider instance is deleted. + void removeProvider( QgsAbstractLabelProvider* provider ); + + //! Lookup provider by its ID + QgsAbstractLabelProvider* providerById( const QString& id ); + //! compute the labeling with given map settings and providers void run( QgsRenderContext& context ); @@ -238,6 +247,8 @@ class CORE_EXPORT QgsLabelingEngineV2 void setFlags( Flags flags ) { mFlags = flags; } Flags flags() const { return mFlags; } + bool testFlag( Flag f ) const { return mFlags.testFlag( f ); } + void setFlag( Flag f, bool enabled ) { if ( enabled ) mFlags |= f; else mFlags &= ~f; } void numCandidatePositions( int& candPoint, int& candLine, int& candPolygon ) { candPoint = mCandPoint; candLine = mCandLine; candPolygon = mCandPolygon; } void setNumCandidatePositions( int candPoint, int candLine, int candPolygon ) { mCandPoint = candPoint; mCandLine = candLine; mCandPolygon = candPolygon; } @@ -245,8 +256,8 @@ class CORE_EXPORT QgsLabelingEngineV2 void setSearchMethod( QgsPalLabeling::Search s ) { mSearchMethod = s; } QgsPalLabeling::Search searchMethod() const { return mSearchMethod; } - protected: - QgsAbstractLabelProvider* providerById( const QString& id ); + void readSettingsFromProject(); + void writeSettingsToProject(); protected: QgsMapSettings mMapSettings; diff --git a/src/core/qgsmaprenderer.h b/src/core/qgsmaprenderer.h index 021d7b6ebae2..dbb166635257 100644 --- a/src/core/qgsmaprenderer.h +++ b/src/core/qgsmaprenderer.h @@ -84,9 +84,15 @@ class CORE_EXPORT QgsLabelingEngineInterface //! called when starting rendering of a layer virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0; //! returns PAL layer settings for a registered layer - virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0; + //! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer() + Q_DECL_DEPRECATED virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0; //! adds a diagram layer to the labeling engine - virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ) + //! @note added in QGIS 2.12 + virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) + { Q_UNUSED( layer ); Q_UNUSED( attrNames ); Q_UNUSED( ctx ); return 0; } + //! adds a diagram layer to the labeling engine + //! @deprecated since 2.12 - use prepareDiagramLayer() + Q_DECL_DEPRECATED virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ) { Q_UNUSED( layer ); Q_UNUSED( s ); return 0; } //! called for every feature virtual void registerFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context = QgsRenderContext(), QString dxfLayer = QString::null ) = 0; diff --git a/src/core/qgsmaprenderercustompainterjob.cpp b/src/core/qgsmaprenderercustompainterjob.cpp index 4996a09f7cee..0529e6367444 100644 --- a/src/core/qgsmaprenderercustompainterjob.cpp +++ b/src/core/qgsmaprenderercustompainterjob.cpp @@ -85,7 +85,9 @@ void QgsMapRendererCustomPainterJob::start() if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) ) { #ifdef LABELING_V2 - mLabelingEngineV2 = new QgsLabelingEngineV2( mSettings ); + mLabelingEngineV2 = new QgsLabelingEngineV2(); + mLabelingEngineV2->readSettingsFromProject(); + mLabelingEngineV2->setMapSettings( mSettings ); #else mLabelingEngine = new QgsPalLabeling; mLabelingEngine->loadEngineSettings(); diff --git a/src/core/qgsmaprendererparalleljob.cpp b/src/core/qgsmaprendererparalleljob.cpp index 64aff1d02c1a..e32f08a183b6 100644 --- a/src/core/qgsmaprendererparalleljob.cpp +++ b/src/core/qgsmaprendererparalleljob.cpp @@ -64,7 +64,9 @@ void QgsMapRendererParallelJob::start() if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) ) { #ifdef LABELING_V2 - mLabelingEngineV2 = new QgsLabelingEngineV2( mSettings ); + mLabelingEngineV2 = new QgsLabelingEngineV2(); + mLabelingEngineV2->readSettingsFromProject(); + mLabelingEngineV2->setMapSettings( mSettings ); #else mLabelingEngine = new QgsPalLabeling; mLabelingEngine->loadEngineSettings(); diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 0d78b2c77a4a..34e64c76743c 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -2361,8 +2363,8 @@ void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, const QgsRende if ( obstacleFeature ) *obstacleFeature = new QgsLabelFeature( lbl->strId(), lbl, QSizeF( 0, 0 ) ); else - if ( !palLayer->registerFeature( lbl->strId(), lbl, 0, 0 ) ) - return; + if ( !palLayer->registerFeature( lbl->strId(), lbl, 0, 0 ) ) + return; } catch ( std::exception &e ) { @@ -3222,42 +3224,14 @@ double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderCon // ------------- QgsPalLabeling::QgsPalLabeling() - : mMapSettings( NULL ) - , mPal( NULL ) - , mDrawLabelRectOnly( false ) - , mShowingCandidates( false ) - , mShowingAllLabels( false ) - , mShowingShadowRects( false ) - , mDrawOutlineLabels( true ) - , mResults( 0 ) + : mEngine( new QgsLabelingEngineV2() ) { - - // find out engine defaults - Pal p; - mCandPoint = p.getPointP(); - mCandLine = p.getLineP(); - mCandPolygon = p.getPolyP(); - mShowingPartialsLabels = p.getShowPartial(); - - switch ( p.getSearch() ) - { - case CHAIN: mSearch = Chain; break; - case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break; - case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break; - case POPMUSIC_TABU_CHAIN: mSearch = Popmusic_Tabu_Chain; break; - case FALP: mSearch = Falp; break; - } } QgsPalLabeling::~QgsPalLabeling() { - // make sure we've freed everything - exit(); - - clearActiveLayers(); - - delete mResults; - mResults = 0; + delete mEngine; + mEngine = 0; } bool QgsPalLabeling::willUseLayer( QgsVectorLayer* layer ) @@ -3287,274 +3261,62 @@ bool QgsPalLabeling::staticWillUseLayer( QgsVectorLayer* layer ) void QgsPalLabeling::clearActiveLayers() { - QHash::iterator lit; - for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit ) - { - clearActiveLayer( lit.key() ); - } - mActiveLayers.clear(); } void QgsPalLabeling::clearActiveLayer( const QString &layerID ) { - QgsPalLayerSettings& lyr = mActiveLayers[layerID]; - - // delete all QgsDataDefined objects (which also deletes their QgsExpression object) - QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::iterator it = lyr.dataDefinedProperties.begin(); - for ( ; it != lyr.dataDefinedProperties.constEnd(); ++it ) - { - delete( it.value() ); - it.value() = 0; - } - lyr.dataDefinedProperties.clear(); + Q_UNUSED( layerID ); } int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) { - Q_ASSERT( mMapSettings != NULL ); - if ( !willUseLayer( layer ) || !layer->labelsEnabled() ) { return 0; } - QgsDebugMsgLevel( "PREPARE LAYER " + layer->id(), 4 ); - - // start with a temporary settings class, find out labeling info - QgsPalLayerSettings lyrTmp; - lyrTmp.readFromLayer( layer ); - - if ( lyrTmp.drawLabels ) - { - if ( lyrTmp.fieldName.isEmpty() ) - { - return 0; - } - - if ( lyrTmp.isExpression ) - { - QgsExpression exp( lyrTmp.fieldName ); - if ( exp.hasEvalError() ) - { - QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 ); - return 0; - } - } - else - { - // If we aren't an expression, we check to see if we can find the column. - if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 ) - { - return 0; - } - } - } - - // add layer settings to the pallabeling hashtable: - mActiveLayers.insert( layer->id(), lyrTmp ); - // start using the reference to the layer in hashtable instead of local instance - QgsPalLayerSettings& lyr = mActiveLayers[layer->id()]; - - lyr.mCurFields = layer->fields(); - - if ( lyrTmp.drawLabels ) - { - // add field indices for label's text, from expression or field - if ( lyr.isExpression ) - { - // prepare expression for use in QgsPalLayerSettings::registerFeature() - QgsExpression* exp = lyr.getLabelExpression(); - exp->prepare( &ctx.expressionContext() ); - if ( exp->hasEvalError() ) - { - QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 ); - } - Q_FOREACH ( const QString& name, exp->referencedColumns() ) - { - QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 ); - attrNames.append( name ); - } - } - else - { - attrNames.append( lyr.fieldName ); - } - - // add field indices of data defined expression or field - QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin(); - for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt ) - { - QgsDataDefined* dd = dIt.value(); - if ( !dd->isActive() ) - { - continue; - } - - // NOTE: the following also prepares any expressions for later use - - // store parameters for data defined expressions - QMap exprParams; - exprParams.insert( "scale", ctx.rendererScale() ); - - dd->setExpressionParams( exprParams ); + QgsVectorLayerLabelProvider* lp = new QgsVectorLayerLabelProvider( layer, false ); + // need to be added before calling prepare() - uses map settings from engine + mEngine->addProvider( lp ); + mLabelProviders[layer->id()] = lp; // fast lookup table by layer ID - // this will return columns for expressions or field name, depending upon what is set to be used - QStringList cols = dd->referencedColumns( ctx.expressionContext() ); // <-- prepares any expressions, too - - //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 ); - Q_FOREACH ( const QString& name, cols ) - { - attrNames.append( name ); - } - } - } - - // how to place the labels - Arrangement arrangement; - switch ( lyr.placement ) + if ( !lp->prepare( ctx, attrNames ) ) { - case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break; - case QgsPalLayerSettings::OverPoint: arrangement = P_POINT_OVER; break; - case QgsPalLayerSettings::Line: arrangement = P_LINE; break; - case QgsPalLayerSettings::Curved: arrangement = P_CURVED; break; - case QgsPalLayerSettings::Horizontal: arrangement = P_HORIZ; break; - case QgsPalLayerSettings::Free: arrangement = P_FREE; break; - default: Q_ASSERT( "unsupported placement" && 0 ); return 0; + mEngine->removeProvider( lp ); + return 0; } - // create the pal layer - double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0 - - Layer* l = mPal->addLayer( layer->id(), - arrangement, - priority, lyr.obstacle, true, lyr.drawLabels, - lyr.displayAll ); - - if ( lyr.placementFlags ) - l->setArrangementFlags(( LineArrangementFlags )lyr.placementFlags ); - - // set label mode (label per feature is the default) - l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature ); + return 1; // init successful +} - // set whether adjacent lines should be merged - l->setMergeConnectedLines( lyr.mergeLines ); +int QgsPalLabeling::prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) +{ + QgsVectorLayerDiagramProvider* dp = new QgsVectorLayerDiagramProvider( layer, false ); + // need to be added before calling prepare() - uses map settings from engine + mEngine->addProvider( dp ); + mDiagramProviders[layer->id()] = dp; // fast lookup table by layer ID - // set obstacle type - switch ( lyr.obstacleType ) + if ( !dp->prepare( ctx, attrNames ) ) { - case PolygonInterior: - l->setObstacleType( pal::PolygonInterior ); - break; - case PolygonBoundary: - l->setObstacleType( pal::PolygonBoundary ); - break; + mEngine->removeProvider( dp ); + return 0; } - // set whether location of centroid must be inside of polygons - l->setCentroidInside( lyr.centroidInside ); - - // set whether labels must fall completely within the polygon - l->setFitInPolygonOnly( lyr.fitInPolygonOnly ); - - // set how to show upside-down labels - Layer::UpsideDownLabels upsdnlabels; - switch ( lyr.upsidedownLabels ) - { - case QgsPalLayerSettings::Upright: upsdnlabels = Layer::Upright; break; - case QgsPalLayerSettings::ShowDefined: upsdnlabels = Layer::ShowDefined; break; - case QgsPalLayerSettings::ShowAll: upsdnlabels = Layer::ShowAll; break; - default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return 0; - } - l->setUpsidedownLabels( upsdnlabels ); - -// // fix for font size in map units causing font to show pointsize at small map scales -// int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx, -// lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points, -// true ); - -// if ( pixelFontSize < 1 ) -// { -// lyr.textFont.setPointSize( 1 ); -// lyr.textFont.setPixelSize( 1 ); -// } -// else -// { -// lyr.textFont.setPixelSize( pixelFontSize ); -// } - -// // scale spacing sizes if using map units -// if ( lyr.fontSizeInMapUnits ) -// { -// double spacingPixelSize; -// if ( lyr.textFont.wordSpacing() != 0 ) -// { -// spacingPixelSize = lyr.textFont.wordSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor(); -// lyr.textFont.setWordSpacing( spacingPixelSize ); -// } - -// if ( lyr.textFont.letterSpacing() != 0 ) -// { -// spacingPixelSize = lyr.textFont.letterSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor(); -// lyr.textFont.setLetterSpacing( QFont::AbsoluteSpacing, spacingPixelSize ); -// } -// } - - //raster and vector scale factors - lyr.vectorScaleFactor = ctx.scaleFactor(); - lyr.rasterCompressFactor = ctx.rasterScaleFactor(); - - // save the pal layer to our layer context (with some additional info) - lyr.palLayer = l; - lyr.fieldIndex = layer->fieldNameIndex( lyr.fieldName ); - - lyr.xform = &mMapSettings->mapToPixel(); - lyr.ct = 0; - if ( mMapSettings->hasCrsTransformEnabled() && ctx.coordinateTransform() ) - lyr.ct = ctx.coordinateTransform()->clone(); - lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 ); - lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 ); - - // rect for clipping - lyr.extentGeom = QgsGeometry::fromRect( mMapSettings->visibleExtent() ); - if ( !qgsDoubleNear( mMapSettings->rotation(), 0.0 ) ) - { - //PAL features are prerotated, so extent also needs to be unrotated - lyr.extentGeom->rotate( -mMapSettings->rotation(), mMapSettings->visibleExtent().center() ); - } - - lyr.mFeatsSendingToPal = 0; - - return 1; // init successful + return 1; } int QgsPalLabeling::addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings *s ) { - double priority = 1 - s->priority / 10.0; // convert 0..10 --> 1..0 - Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), pal::Arrangement( s->placement ), priority, s->obstacle, true, true ); - l->setArrangementFlags(( LineArrangementFlags )s->placementFlags ); - - mActiveDiagramLayers.insert( layer->id(), *s ); - // initialize the local copy - QgsDiagramLayerSettings& s2 = mActiveDiagramLayers[layer->id()]; - - s2.palLayer = l; - s2.ct = 0; - if ( mMapSettings->hasCrsTransformEnabled() ) - s2.ct = new QgsCoordinateTransform( layer->crs(), mMapSettings->destinationCrs() ); - - s2.xform = &mMapSettings->mapToPixel(); - - s2.fields = layer->fields(); - - s2.renderer = layer->diagramRenderer()->clone(); - - return 1; + QgsDebugMsg( "Called addDiagramLayer()... need to use prepareDiagramLayer() instead!" ); + Q_UNUSED( layer ); + Q_UNUSED( s ); + return 0; } void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, const QgsRenderContext& context, QString dxfLayer ) { - QgsPalLayerSettings& lyr = mActiveLayers[layerID]; - lyr.registerFeature( f, context, dxfLayer ); + if ( QgsVectorLayerLabelProvider* provider = mLabelProviders.value( layerID, 0 ) ) + provider->registerFeature( f, context ); } bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, QgsGeometry* clipGeometry ) @@ -3739,131 +3501,8 @@ bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, const void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context ) { - //get diagram layer settings, diagram renderer - QHash::iterator layerIt = mActiveDiagramLayers.find( layerID ); - if ( layerIt == mActiveDiagramLayers.constEnd() ) - { - return; - } - - QgsDiagramRendererV2* dr = layerIt.value().renderer; - if ( dr ) - { - QList settingList = dr->diagramSettings(); - if ( settingList.size() > 0 ) - { - double minScale = settingList.at( 0 ).minScaleDenominator; - if ( minScale > 0 && context.rendererScale() < minScale ) - { - return; - } - - double maxScale = settingList.at( 0 ).maxScaleDenominator; - if ( maxScale > 0 && context.rendererScale() > maxScale ) - { - return; - } - } - } - - //convert geom to geos - const QgsGeometry* geom = feat.constGeometry(); - QScopedPointer extentGeom( QgsGeometry::fromRect( mMapSettings->visibleExtent() ) ); - if ( !qgsDoubleNear( mMapSettings->rotation(), 0.0 ) ) - { - //PAL features are prerotated, so extent also needs to be unrotated - extentGeom->rotate( -mMapSettings->rotation(), mMapSettings->visibleExtent().center() ); - } - - const GEOSGeometry* geos_geom = 0; - QScopedPointer preparedGeom; - if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, layerIt.value().ct, extentGeom.data() ) ) - { - preparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, layerIt.value().ct, extentGeom.data() ) ); - if ( !preparedGeom.data() ) - return; - geos_geom = preparedGeom.data()->asGeos(); - } - else - { - geos_geom = geom->asGeos(); - } - - if ( geos_geom == 0 ) - { - return; // invalid geometry - } - - //create PALGeometry with diagram = true - QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ) ); - lbl->setIsDiagram( true ); - - // record the created geometry - it will be deleted at the end. - layerIt.value().geometries.append( lbl ); - - double diagramWidth = 0; - double diagramHeight = 0; - if ( dr ) - { - QSizeF diagSize = dr->sizeMapUnits( feat, context ); - if ( diagSize.isValid() ) - { - diagramWidth = diagSize.width(); - diagramHeight = diagSize.height(); - } - - //append the diagram attributes to lbl - lbl->setDiagramAttributes( feat.attributes() ); - } - - // feature to the layer - bool alwaysShow = layerIt.value().showAll; - int ddColX = layerIt.value().xPosColumn; - int ddColY = layerIt.value().yPosColumn; - double ddPosX = 0.0; - double ddPosY = 0.0; - bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); - if ( ddPos ) - { - bool posXOk, posYOk; - ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ); - ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ); - if ( !posXOk || !posYOk ) - { - ddPos = false; - } - else - { - const QgsCoordinateTransform* ct = layerIt.value().ct; - if ( ct ) - { - double z = 0; - ct->transformInPlace( ddPosX, ddPosY, z ); - } - //data defined diagram position is always centered - ddPosX -= diagramWidth / 2.0; - ddPosY -= diagramHeight / 2.0; - } - } - - try - { - if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos, 0.0, true, 0, 0, 0, 0, alwaysShow ) ) - { - return; - } - } - catch ( std::exception &e ) - { - Q_UNUSED( e ); - QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feat.id() ) + QString::fromLatin1( e.what() ), 4 ); - return; - } - - pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() ); - QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 ); - QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 ); - palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist ); + if ( QgsVectorLayerDiagramProvider* provider = mDiagramProviders.value( layerID, 0 ) ) + provider->registerFeature( feat, context ); } @@ -3872,56 +3511,19 @@ void QgsPalLabeling::init( QgsMapRenderer* mr ) init( mr->mapSettings() ); } + void QgsPalLabeling::init( const QgsMapSettings& mapSettings ) { - mMapSettings = &mapSettings; - - // delete if exists already - if ( mPal ) - delete mPal; - - mPal = new Pal; - - SearchMethod s; - switch ( mSearch ) - { - default: - case Chain: s = CHAIN; break; - case Popmusic_Tabu: s = POPMUSIC_TABU; break; - case Popmusic_Chain: s = POPMUSIC_CHAIN; break; - case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break; - case Falp: s = FALP; break; - } - mPal->setSearch( s ); - - // set number of candidates generated per feature - mPal->setPointP( mCandPoint ); - mPal->setLineP( mCandLine ); - mPal->setPolyP( mCandPolygon ); - - mPal->setShowPartial( mShowingPartialsLabels ); - - clearActiveLayers(); // free any previous QgsDataDefined objects - mActiveDiagramLayers.clear(); + mEngine->setMapSettings( mapSettings ); } void QgsPalLabeling::exit() { - delete mPal; - mPal = NULL; - mMapSettings = NULL; } QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName ) { - QHash::iterator lit; - for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit ) - { - if ( lit.key() == layerName ) - { - return lit.value(); - } - } + Q_UNUSED( layerName ); return mInvalidLayerSettings; } @@ -4238,344 +3840,109 @@ void QgsPalLabeling::dataDefinedDropShadow( QgsPalLayerSettings& tmpLyr, } -// helper function for checking for job cancellation within PAL -static bool _palIsCancelled( void* ctx ) -{ - return (( QgsRenderContext* ) ctx )->renderingStopped(); -} void QgsPalLabeling::drawLabeling( QgsRenderContext& context ) { - Q_ASSERT( mMapSettings != NULL ); - QPainter* painter = context.painter(); - - QgsGeometry* extentGeom( QgsGeometry::fromRect( mMapSettings->visibleExtent() ) ); - if ( !qgsDoubleNear( mMapSettings->rotation(), 0.0 ) ) - { - //PAL features are prerotated, so extent also needs to be unrotated - extentGeom->rotate( -mMapSettings->rotation(), mMapSettings->visibleExtent().center() ); - } - - QgsRectangle extent = extentGeom->boundingBox(); - delete extentGeom; - - mPal->registerCancellationCallback( &_palIsCancelled, &context ); - - delete mResults; - mResults = new QgsLabelingResults; - - QTime t; - t.start(); - - // do the labeling itself - double bbox[] = { extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() }; - - std::list* labels; - pal::Problem *problem; - try - { - problem = mPal->extractProblem( bbox ); - } - catch ( std::exception& e ) - { - Q_UNUSED( e ); - QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 ); - //mActiveLayers.clear(); // clean up - return; - } - - if ( context.renderingStopped() ) - { - delete problem; - deleteTemporaryData(); - return; // it has been cancelled - } - -#if 1 // XXX strk - // features are pre-rotated but not scaled/translated, - // so we only disable rotation here. Ideally, they'd be - // also pre-scaled/translated, as suggested here: - // http://hub.qgis.org/issues/11856 - QgsMapToPixel xform = mMapSettings->mapToPixel(); - xform.setMapRotation( 0, 0, 0 ); -#else - const QgsMapToPixel& xform = mMapSettings->mapToPixel(); -#endif - - // draw rectangles with all candidates - // this is done before actual solution of the problem - // before number of candidates gets reduced - mCandidates.clear(); - if ( mShowingCandidates && problem ) - { - painter->setBrush( Qt::NoBrush ); - for ( int i = 0; i < problem->getNumFeatures(); i++ ) - { - for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ ) - { - pal::LabelPosition* lp = problem->getFeatureCandidate( i, j ); - - drawLabelCandidateRect( lp, painter, &xform ); - } - } - } - - // find the solution - labels = mPal->solveProblem( problem, mShowingAllLabels ); - - QgsDebugMsgLevel( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 ); - t.restart(); - - if ( context.renderingStopped() ) - { - delete problem; - delete labels; - deleteTemporaryData(); - return; - } - - painter->setRenderHint( QPainter::Antialiasing ); - - // draw the labels - std::list::iterator it = labels->begin(); - for ( ; it != labels->end(); ++it ) - { - if ( context.renderingStopped() ) - break; - - QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() ); - if ( !palGeometry ) - { - continue; - } - - //layer names - QString layerName = ( *it )->getLayerName(); - if ( palGeometry->isDiagram() ) - { - QgsFeature feature; - //render diagram - QHash::iterator dit = mActiveDiagramLayers.begin(); - for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit ) - { - if ( QString( dit.key() + "d" ) == layerName ) - { - feature.setFields( dit.value().fields ); - palGeometry->feature( feature ); - - //calculate top-left point for diagram - //first, calculate the centroid of the label (accounts for PAL creating - //rotated labels when we do not want to draw the diagrams rotated) - double centerX = 0; - double centerY = 0; - for ( int i = 0; i < 4; ++i ) - { - centerX += ( *it )->getX( i ); - centerY += ( *it )->getY( i ); - } - QgsPoint outPt( centerX / 4.0, centerY / 4.0 ); - //then, calculate the top left point for the diagram with this center position - QgsPoint centerPt = xform.transform( outPt.x() - ( *it )->getWidth() / 2, - outPt.y() - ( *it )->getHeight() / 2 ); - - dit.value().renderer->renderDiagram( feature, context, centerPt.toQPointF() ); - } - } - - //insert into label search tree to manipulate position interactively - if ( mResults->mLabelSearchTree ) - { - //for diagrams, remove the additional 'd' at the end of the layer id - QString layerId = layerName; - layerId.chop( 1 ); - mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerId, QString(), QFont(), true, false ); - } - continue; - } - - const QgsPalLayerSettings& lyr = layer( layerName ); - if ( !lyr.drawLabels ) - continue; - - // Copy to temp, editable layer settings - // these settings will be changed by any data defined values, then used for rendering label components - // settings may be adjusted during rendering of components - QgsPalLayerSettings tmpLyr( lyr ); - - // apply any previously applied data defined settings for the label - const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues = palGeometry->dataDefinedValues(); - - //font - QFont dFont = palGeometry->definedFont(); - QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString() ).arg( tmpLyr.textFont.styleName() ), 4 ); - QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString() ).arg( dFont.styleName() ), 4 ); - tmpLyr.textFont = dFont; - - if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiFollowPlacement ) - { - //calculate font alignment based on label quadrant - switch (( *it )->getQuadrant() ) - { - case LabelPosition::QuadrantAboveLeft: - case LabelPosition::QuadrantLeft: - case LabelPosition::QuadrantBelowLeft: - tmpLyr.multilineAlign = QgsPalLayerSettings::MultiRight; - break; - case LabelPosition::QuadrantAbove: - case LabelPosition::QuadrantOver: - case LabelPosition::QuadrantBelow: - tmpLyr.multilineAlign = QgsPalLayerSettings::MultiCenter; - break; - case LabelPosition::QuadrantAboveRight: - case LabelPosition::QuadrantRight: - case LabelPosition::QuadrantBelowRight: - tmpLyr.multilineAlign = QgsPalLayerSettings::MultiLeft; - break; - } - } - - // update tmpLyr with any data defined text style values - dataDefinedTextStyle( tmpLyr, ddValues ); - - // update tmpLyr with any data defined text buffer values - dataDefinedTextBuffer( tmpLyr, ddValues ); - - // update tmpLyr with any data defined text formatting values - dataDefinedTextFormatting( tmpLyr, ddValues ); - - // update tmpLyr with any data defined shape background values - dataDefinedShapeBackground( tmpLyr, ddValues ); - - // update tmpLyr with any data defined drop shadow values - dataDefinedDropShadow( tmpLyr, ddValues ); + mEngine->run( context ); +} +void QgsPalLabeling::deleteTemporaryData() +{ +} - tmpLyr.showingShadowRects = mShowingShadowRects; +QList QgsPalLabeling::labelsAtPosition( const QgsPoint& p ) +{ + return mEngine->results() ? mEngine->results()->labelsAtPosition( p ) : QList(); +} - // Render the components of a label in reverse order - // (backgrounds -> text) +QList QgsPalLabeling::labelsWithinRect( const QgsRectangle& r ) +{ + return mEngine->results() ? mEngine->results()->labelsWithinRect( r ) : QList(); +} - if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowLowest ) - { - if ( tmpLyr.shapeDraw ) - { - tmpLyr.shadowUnder = QgsPalLayerSettings::ShadowShape; - } - else if ( tmpLyr.bufferDraw ) - { - tmpLyr.shadowUnder = QgsPalLayerSettings::ShadowBuffer; - } - else - { - tmpLyr.shadowUnder = QgsPalLayerSettings::ShadowText; - } - } +QgsLabelingResults *QgsPalLabeling::takeResults() +{ + return mEngine->takeResults(); +} - if ( tmpLyr.shapeDraw ) - { - drawLabel( *it, context, tmpLyr, LabelShape ); - } +void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon ) +{ + mEngine->numCandidatePositions( candPoint, candLine, candPolygon ); +} - if ( tmpLyr.bufferDraw ) - { - drawLabel( *it, context, tmpLyr, LabelBuffer ); - } +void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon ) +{ + mEngine->setNumCandidatePositions( candPoint, candLine, candPolygon ); +} - drawLabel( *it, context, tmpLyr, LabelText ); +void QgsPalLabeling::setSearchMethod( QgsPalLabeling::Search s ) +{ + mEngine->setSearchMethod( s ); +} - if ( mResults->mLabelSearchTree ) - { - QString labeltext = (( QgsPalGeometry* )( *it )->getFeaturePart()->getUserGeometry() )->text(); - mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerName, labeltext, dFont, false, palGeometry->isPinned() ); - } - } +QgsPalLabeling::Search QgsPalLabeling::searchMethod() const +{ + return mEngine->searchMethod(); +} - // Reset composition mode for further drawing operations - painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); +bool QgsPalLabeling::isShowingCandidates() const +{ + return mEngine->testFlag( QgsLabelingEngineV2::DrawCandidates ); +} - QgsDebugMsgLevel( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 ); +void QgsPalLabeling::setShowingCandidates( bool showing ) +{ + mEngine->setFlag( QgsLabelingEngineV2::DrawCandidates, showing ); +} - delete problem; - delete labels; - deleteTemporaryData(); +bool QgsPalLabeling::isShowingShadowRectangles() const +{ + return mEngine->testFlag( QgsLabelingEngineV2::DrawShadowRects ); } -void QgsPalLabeling::deleteTemporaryData() +void QgsPalLabeling::setShowingShadowRectangles( bool showing ) { - // delete all allocated geometries for features - QHash::iterator lit; - for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit ) - { - QgsPalLayerSettings& lyr = lit.value(); - for ( QList::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git ) - delete *git; - if ( lyr.limitNumLabels ) - { - QgsDebugMsgLevel( QString( "mFeaturesToLabel: %1" ).arg( lyr.mFeaturesToLabel ), 4 ); - QgsDebugMsgLevel( QString( "maxNumLabels: %1" ).arg( lyr.maxNumLabels ), 4 ); - QgsDebugMsgLevel( QString( "mFeatsSendingToPal: %1" ).arg( lyr.mFeatsSendingToPal ), 4 ); - QgsDebugMsgLevel( QString( "mFeatsRegPal: %1" ).arg( lyr.geometries.count() ), 4 ); - } - lyr.geometries.clear(); - } + mEngine->setFlag( QgsLabelingEngineV2::DrawShadowRects, showing ); +} - //delete all allocated geometries for diagrams - QHash::iterator dIt = mActiveDiagramLayers.begin(); - for ( ; dIt != mActiveDiagramLayers.end(); ++dIt ) - { - QgsDiagramLayerSettings& dls = dIt.value(); - for ( QList::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git ) - { - delete *git; - } - dls.geometries.clear(); - } +bool QgsPalLabeling::isShowingAllLabels() const +{ + return mEngine->testFlag( QgsLabelingEngineV2::UseAllLabels ); } -QList QgsPalLabeling::labelsAtPosition( const QgsPoint& p ) +void QgsPalLabeling::setShowingAllLabels( bool showing ) { - return mResults ? mResults->labelsAtPosition( p ) : QList(); + mEngine->setFlag( QgsLabelingEngineV2::UseAllLabels, showing ); } -QList QgsPalLabeling::labelsWithinRect( const QgsRectangle& r ) +bool QgsPalLabeling::isShowingPartialsLabels() const { - return mResults ? mResults->labelsWithinRect( r ) : QList(); + return mEngine->testFlag( QgsLabelingEngineV2::UsePartialCandidates ); } -QgsLabelingResults *QgsPalLabeling::takeResults() +void QgsPalLabeling::setShowingPartialsLabels( bool showing ) { - if ( mResults ) - { - QgsLabelingResults* tmp = mResults; - mResults = 0; - return tmp; // ownership passed to the caller - } - else - return 0; + mEngine->setFlag( QgsLabelingEngineV2::UsePartialCandidates, showing ); } -void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon ) +bool QgsPalLabeling::isDrawingOutlineLabels() const { - candPoint = mCandPoint; - candLine = mCandLine; - candPolygon = mCandPolygon; + return mEngine->testFlag( QgsLabelingEngineV2::RenderOutlineLabels ); } -void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon ) +void QgsPalLabeling::setDrawingOutlineLabels( bool outline ) { - mCandPoint = candPoint; - mCandLine = candLine; - mCandPolygon = candPolygon; + mEngine->setFlag( QgsLabelingEngineV2::RenderOutlineLabels, outline ); } -void QgsPalLabeling::setSearchMethod( QgsPalLabeling::Search s ) +bool QgsPalLabeling::drawLabelRectOnly() const { - mSearch = s; + return mEngine->testFlag( QgsLabelingEngineV2::DrawLabelRectOnly ); } -QgsPalLabeling::Search QgsPalLabeling::searchMethod() const +void QgsPalLabeling::setDrawLabelRectOnly( bool drawRect ) { - return mSearch; + mEngine->setFlag( QgsLabelingEngineV2::DrawLabelRectOnly, drawRect ); } void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform, QList* candidates ) @@ -4639,286 +4006,6 @@ void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* p void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType, double dpiRatio ) { - // NOTE: this is repeatedly called for multi-part labels - QPainter* painter = context.painter(); -#if 1 // XXX strk - // features are pre-rotated but not scaled/translated, - // so we only disable rotation here. Ideally, they'd be - // also pre-scaled/translated, as suggested here: - // http://hub.qgis.org/issues/11856 - QgsMapToPixel xform = context.mapToPixel(); - xform.setMapRotation( 0, 0, 0 ); -#else - const QgsMapToPixel& xform = context.mapToPixel(); -#endif - - QgsLabelComponent component; - component.setDpiRatio( dpiRatio ); - - QgsPoint outPt = xform.transform( label->getX(), label->getY() ); -// QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() ); -// QRectF labelRect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() ); - - component.setOrigin( outPt ); - component.setRotation( label->getAlpha() ); - - if ( mDrawLabelRectOnly ) - { - //debugging rect - if ( drawType != QgsPalLabeling::LabelText ) - return; - - QgsPoint outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() ); - QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() ); - painter->save(); - painter->setRenderHint( QPainter::Antialiasing, false ); - painter->translate( QPointF( outPt.x(), outPt.y() ) ); - painter->rotate( -label->getAlpha() * 180 / M_PI ); - - if ( label->conflictsWithObstacle() ) - { - painter->setBrush( QColor( 255, 0, 0, 100 ) ); - painter->setPen( QColor( 255, 0, 0, 150 ) ); - } - else - { - painter->setBrush( QColor( 0, 255, 0, 100 ) ); - painter->setPen( QColor( 0, 255, 0, 150 ) ); - } - - painter->drawRect( rect ); - painter->restore(); - - if ( label->getNextPart() ) - drawLabel( label->getNextPart(), context, tmpLyr, drawType, dpiRatio ); - - return; - } - - if ( drawType == QgsPalLabeling::LabelShape ) - { - // get rotated label's center point - QgsPoint centerPt( outPt ); - QgsPoint outPt2 = xform.transform( label->getX() + label->getWidth() / 2, - label->getY() + label->getHeight() / 2 ); - - double xc = outPt2.x() - outPt.x(); - double yc = outPt2.y() - outPt.y(); - - double angle = -label->getAlpha(); - double xd = xc * cos( angle ) - yc * sin( angle ); - double yd = xc * sin( angle ) + yc * cos( angle ); - - centerPt.setX( centerPt.x() + xd ); - centerPt.setY( centerPt.y() + yd ); - - component.setCenter( centerPt ); - component.setSize( QgsPoint( label->getWidth(), label->getHeight() ) ); - - drawLabelBackground( context, component, tmpLyr ); - } - - else if ( drawType == QgsPalLabeling::LabelBuffer - || drawType == QgsPalLabeling::LabelText ) - { - - // TODO: optimize access :) - QString txt = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text( label->getPartId() ); - QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics(); - - //add the direction symbol if needed - if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && - tmpLyr.addDirectionSymbol ) - { - bool prependSymb = false; - QString symb = tmpLyr.rightDirectionSymbol; - - if ( label->getReversed() ) - { - prependSymb = true; - symb = tmpLyr.leftDirectionSymbol; - } - - if ( tmpLyr.reverseDirectionSymbol ) - { - if ( symb == tmpLyr.rightDirectionSymbol ) - { - prependSymb = true; - symb = tmpLyr.leftDirectionSymbol; - } - else - { - prependSymb = false; - symb = tmpLyr.rightDirectionSymbol; - } - } - - if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove ) - { - prependSymb = true; - symb = symb + QString( "\n" ); - } - else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow ) - { - prependSymb = false; - symb = QString( "\n" ) + symb; - } - - if ( prependSymb ) - { - txt.prepend( symb ); - } - else - { - txt.append( symb ); - } - } - - //QgsDebugMsgLevel( "drawLabel " + txt, 4 ); - QStringList multiLineList = QgsPalLabeling::splitToLines( txt, tmpLyr.wrapChar ); - int lines = multiLineList.size(); - - double labelWidest = 0.0; - for ( int i = 0; i < lines; ++i ) - { - double labelWidth = labelfm->width( multiLineList.at( i ) ); - if ( labelWidth > labelWidest ) - { - labelWidest = labelWidth; - } - } - - double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline - // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight ); - - // needed to move bottom of text's descender to within bottom edge of label - double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough - - for ( int i = 0; i < lines; ++i ) - { - painter->save(); -#if 0 // TODO: generalize some of this - LabelPosition* lp = label; - double w = lp->getWidth(); - double h = lp->getHeight(); - double cx = lp->getX() + w / 2.0; - double cy = lp->getY() + h / 2.0; - double scale = 1.0 / xform->mapUnitsPerPixel(); - double rotation = xform->mapRotation(); - double sw = w * scale; - double sh = h * scale; - QRectF rect( -sw / 2, -sh / 2, sw, sh ); - painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() ); - if ( rotation ) - { - // Only if not horizontal - if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT && - lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER && - lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ ) - { - painter->rotate( rotation ); - } - } - painter->translate( rect.bottomLeft() ); - painter->rotate( -lp->getAlpha() * 180 / M_PI ); -#else - painter->translate( QPointF( outPt.x(), outPt.y() ) ); - painter->rotate( -label->getAlpha() * 180 / M_PI ); -#endif - - // scale down painter: the font size has been multiplied by raster scale factor - // to workaround a Qt font scaling bug with small font sizes - painter->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor ); - - // figure x offset for horizontal alignment of multiple lines - double xMultiLineOffset = 0.0; - double labelWidth = labelfm->width( multiLineList.at( i ) ); - if ( lines > 1 && tmpLyr.multilineAlign != QgsPalLayerSettings::MultiLeft ) - { - double labelWidthDiff = labelWidest - labelWidth; - if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiCenter ) - { - labelWidthDiff /= 2; - } - xMultiLineOffset = labelWidthDiff; - //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 ); - } - - double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * tmpLyr.multilineHeight; - painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) ); - - component.setText( multiLineList.at( i ) ); - component.setSize( QgsPoint( labelWidth, labelHeight ) ); - component.setOffset( QgsPoint( 0.0, -ascentOffset ) ); - component.setRotation( -component.rotation() * 180 / M_PI ); - component.setRotationOffset( 0.0 ); - - if ( drawType == QgsPalLabeling::LabelBuffer ) - { - // draw label's buffer - drawLabelBuffer( context, component, tmpLyr ); - } - else - { - // draw label's text, QPainterPath method - QPainterPath path; - path.setFillRule( Qt::WindingFill ); - path.addText( 0, 0, tmpLyr.textFont, component.text() ); - - // store text's drawing in QPicture for drop shadow call - QPicture textPict; - QPainter textp; - textp.begin( &textPict ); - textp.setPen( Qt::NoPen ); - textp.setBrush( tmpLyr.textColor ); - textp.drawPath( path ); - // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture? - // e.g. some capitalization options, but not others - //textp.setFont( tmpLyr.textFont ); - //textp.setPen( tmpLyr.textColor ); - //textp.drawText( 0, 0, component.text() ); - textp.end(); - - if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowText ) - { - component.setPicture( &textPict ); - component.setPictureBuffer( 0.0 ); // no pen width to deal with - component.setOrigin( QgsPoint( 0.0, 0.0 ) ); - - drawLabelShadow( context, component, tmpLyr ); - } - - // paint the text - if ( context.useAdvancedEffects() ) - { - painter->setCompositionMode( tmpLyr.blendMode ); - } - - // scale for any print output or image saving @ specific dpi - painter->scale( component.dpiRatio(), component.dpiRatio() ); - - if ( mDrawOutlineLabels ) - { - // draw outlined text - _fixQPictureDPI( painter ); - painter->drawPicture( 0, 0, textPict ); - } - else - { - // draw text as text (for SVG and PDF exports) - painter->setFont( tmpLyr.textFont ); - painter->setPen( tmpLyr.textColor ); - painter->setRenderHint( QPainter::TextAntialiasing ); - painter->drawText( 0, 0, component.text() ); - } - } - painter->restore(); - } - } - - // NOTE: this used to be within above multi-line loop block, at end. (a mistake since 2010? [LS]) - if ( label->getNextPart() ) - drawLabel( label->getNextPart(), context, tmpLyr, drawType, dpiRatio ); } void QgsPalLabeling::drawLabelBuffer( QgsRenderContext& context, @@ -5424,43 +4511,12 @@ void QgsPalLabeling::drawLabelShadow( QgsRenderContext& context, void QgsPalLabeling::loadEngineSettings() { - // start with engine defaults for new project, or project that has no saved settings - Pal p; - bool saved = false; - mSearch = ( QgsPalLabeling::Search )( QgsProject::instance()->readNumEntry( - "PAL", "/SearchMethod", ( int )p.getSearch(), &saved ) ); - mCandPoint = QgsProject::instance()->readNumEntry( - "PAL", "/CandidatesPoint", p.getPointP(), &saved ); - mCandLine = QgsProject::instance()->readNumEntry( - "PAL", "/CandidatesLine", p.getLineP(), &saved ); - mCandPolygon = QgsProject::instance()->readNumEntry( - "PAL", "/CandidatesPolygon", p.getPolyP(), &saved ); - mShowingCandidates = QgsProject::instance()->readBoolEntry( - "PAL", "/ShowingCandidates", false, &saved ); - mDrawLabelRectOnly = QgsProject::instance()->readBoolEntry( - "PAL", "/DrawRectOnly", false, &saved ); - mShowingShadowRects = QgsProject::instance()->readBoolEntry( - "PAL", "/ShowingShadowRects", false, &saved ); - mShowingAllLabels = QgsProject::instance()->readBoolEntry( - "PAL", "/ShowingAllLabels", false, &saved ); - mShowingPartialsLabels = QgsProject::instance()->readBoolEntry( - "PAL", "/ShowingPartialsLabels", p.getShowPartial(), &saved ); - mDrawOutlineLabels = QgsProject::instance()->readBoolEntry( - "PAL", "/DrawOutlineLabels", true, &saved ); + mEngine->readSettingsFromProject(); } void QgsPalLabeling::saveEngineSettings() { - QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearch ); - QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint ); - QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine ); - QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon ); - QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mShowingCandidates ); - QgsProject::instance()->writeEntry( "PAL", "/DrawRectOnly", mDrawLabelRectOnly ); - QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mShowingShadowRects ); - QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mShowingAllLabels ); - QgsProject::instance()->writeEntry( "PAL", "/ShowingPartialsLabels", mShowingPartialsLabels ); - QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", mDrawOutlineLabels ); + mEngine->writeSettingsToProject(); } void QgsPalLabeling::clearEngineSettings() @@ -5479,12 +4535,12 @@ void QgsPalLabeling::clearEngineSettings() QgsLabelingEngineInterface* QgsPalLabeling::clone() { QgsPalLabeling* lbl = new QgsPalLabeling(); - lbl->mShowingAllLabels = mShowingAllLabels; - lbl->mShowingCandidates = mShowingCandidates; - lbl->mDrawLabelRectOnly = mDrawLabelRectOnly; - lbl->mShowingShadowRects = mShowingShadowRects; - lbl->mShowingPartialsLabels = mShowingPartialsLabels; - lbl->mDrawOutlineLabels = mDrawOutlineLabels; + lbl->setShowingAllLabels( isShowingAllLabels() ); + lbl->setShowingCandidates( isShowingCandidates() ); + lbl->setDrawLabelRectOnly( drawLabelRectOnly() ); + lbl->setShowingShadowRectangles( isShowingShadowRectangles() ); + lbl->setShowingPartialsLabels( isShowingPartialsLabels() ); + lbl->setDrawingOutlineLabels( isDrawingOutlineLabels() ); return lbl; } diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h index e31c8abaf53f..74380daba27a 100644 --- a/src/core/qgspallabeling.h +++ b/src/core/qgspallabeling.h @@ -56,6 +56,9 @@ class QgsCoordinateTransform; class QgsLabelSearchTree; class QgsMapSettings; class QgsLabelFeature; +class QgsLabelingEngineV2; +class QgsVectorLayerLabelProvider; +class QgsVectorLayerDiagramProvider; class CORE_EXPORT QgsPalLayerSettings { @@ -781,7 +784,8 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface QgsPalLabeling(); ~QgsPalLabeling(); - QgsPalLayerSettings& layer( const QString& layerName ) override; + //! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer() + Q_DECL_DEPRECATED QgsPalLayerSettings& layer( const QString& layerName ) override; void numCandidatePositions( int& candPoint, int& candLine, int& candPolygon ); void setNumCandidatePositions( int candPoint, int candLine, int candPolygon ); @@ -791,29 +795,30 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface void setSearchMethod( Search s ); Search searchMethod() const; - bool isShowingCandidates() const { return mShowingCandidates; } - void setShowingCandidates( bool showing ) { mShowingCandidates = showing; } - const QList& candidates() { return mCandidates; } + bool isShowingCandidates() const; + void setShowingCandidates( bool showing ); + //! @deprecated since 2.12 + Q_DECL_DEPRECATED const QList& candidates() { return mCandidates; } - bool isShowingShadowRectangles() const { return mShowingShadowRects; } - void setShowingShadowRectangles( bool showing ) { mShowingShadowRects = showing; } + bool isShowingShadowRectangles() const; + void setShowingShadowRectangles( bool showing ); - bool isShowingAllLabels() const { return mShowingAllLabels; } - void setShowingAllLabels( bool showing ) { mShowingAllLabels = showing; } + bool isShowingAllLabels() const; + void setShowingAllLabels( bool showing ); - bool isShowingPartialsLabels() const { return mShowingPartialsLabels; } - void setShowingPartialsLabels( bool showing ) { mShowingPartialsLabels = showing; } + bool isShowingPartialsLabels() const; + void setShowingPartialsLabels( bool showing ); //! @note added in 2.4 - bool isDrawingOutlineLabels() const { return mDrawOutlineLabels; } - void setDrawingOutlineLabels( bool outline ) { mDrawOutlineLabels = outline; } + bool isDrawingOutlineLabels() const; + void setDrawingOutlineLabels( bool outline ); /** Returns whether the engine will only draw the outline rectangles of labels, * not the label contents themselves. Used for debugging and testing purposes. * @see setDrawLabelRectOnly * @note added in QGIS 2.12 */ - bool drawLabelRectOnly() const { return mDrawLabelRectOnly; } + bool drawLabelRectOnly() const; /** Sets whether the engine should only draw the outline rectangles of labels, * not the label contents themselves. Used for debugging and testing purposes. @@ -821,7 +826,7 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface * @see drawLabelRectOnly * @note added in QGIS 2.12 */ - void setDrawLabelRectOnly( bool drawRect ) { mDrawLabelRectOnly = drawRect; } + void setDrawLabelRectOnly( bool drawRect ); // implemented methods from labeling engine interface @@ -845,7 +850,11 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface //! hook called when drawing layer before issuing select() virtual int prepareLayer( QgsVectorLayer* layer, QStringList &attrNames, QgsRenderContext& ctx ) override; //! adds a diagram layer to the labeling engine - virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings *s ) override; + //! @note added in QGIS 2.12 + virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) override; + //! adds a diagram layer to the labeling engine + //! @deprecated since 2.12 - use prepareDiagramLayer() + Q_DECL_DEPRECATED virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings *s ) override; /** Register a feature for labelling. * @param layerID string identifying layer associated with label @@ -976,31 +985,18 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface */ static bool checkMinimumSizeMM( const QgsRenderContext &context, const QgsGeometry *geom, double minSize ); - // hashtable of layer settings, being filled during labeling (key = layer ID) - QHash mActiveLayers; - // hashtable of active diagram layers (key = layer ID) - QHash mActiveDiagramLayers; + //! hashtable of label providers, being filled during labeling (key = layer ID) + QHash mLabelProviders; + //! hashtable of diagram providers (key = layer ID) + QHash mDiagramProviders; QgsPalLayerSettings mInvalidLayerSettings; - const QgsMapSettings* mMapSettings; - int mCandPoint, mCandLine, mCandPolygon; - Search mSearch; - - pal::Pal* mPal; + //! New labeling engine to interface with PAL + QgsLabelingEngineV2* mEngine; // list of candidates from last labeling QList mCandidates; - //! Whether to only draw the label rect and not the actual label text (used for unit tests) - bool mDrawLabelRectOnly; - bool mShowingCandidates; - bool mShowingAllLabels; // whether to avoid collisions or not - bool mShowingShadowRects; // whether to show debugging rectangles for drop shadows - bool mShowingPartialsLabels; // whether to avoid partials labels or not - bool mDrawOutlineLabels; // whether to draw labels as text or outlines - - QgsLabelingResults* mResults; - friend class QgsPalLayerSettings; }; Q_NOWARN_DEPRECATED_POP diff --git a/src/core/qgsvectorlayerdiagramprovider.cpp b/src/core/qgsvectorlayerdiagramprovider.cpp index e507d10da8b6..2a8a64565726 100644 --- a/src/core/qgsvectorlayerdiagramprovider.cpp +++ b/src/core/qgsvectorlayerdiagramprovider.cpp @@ -45,14 +45,14 @@ QgsVectorLayerDiagramProvider::QgsVectorLayerDiagramProvider( } -QgsVectorLayerDiagramProvider::QgsVectorLayerDiagramProvider( QgsVectorLayer* layer ) +QgsVectorLayerDiagramProvider::QgsVectorLayerDiagramProvider( QgsVectorLayer* layer, bool ownFeatureLoop ) : mSettings( *layer->diagramLayerSettings() ) , mDiagRenderer( layer->diagramRenderer()->clone() ) , mLayerId( layer->id() ) , mFields( layer->fields() ) , mLayerCrs( layer->crs() ) - , mSource( new QgsVectorLayerFeatureSource( layer ) ) - , mOwnsSource( true ) + , mSource( ownFeatureLoop ? new QgsVectorLayerFeatureSource( layer ) : 0 ) + , mOwnsSource( ownFeatureLoop ) { init(); } @@ -81,67 +81,18 @@ QString QgsVectorLayerDiagramProvider::id() const return mLayerId + "d"; } -QList QgsVectorLayerDiagramProvider::labelFeatures( const QgsMapSettings& mapSettings, const QgsRenderContext& context ) +QList QgsVectorLayerDiagramProvider::labelFeatures( const QgsRenderContext& context ) { - - QgsDiagramLayerSettings& s2 = mSettings; - - s2.ct = 0; - if ( mapSettings.hasCrsTransformEnabled() ) - s2.ct = new QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() ); - - s2.xform = &mapSettings.mapToPixel(); - - s2.fields = mFields; - - s2.renderer = mDiagRenderer; - - const QgsDiagramRendererV2* diagRenderer = s2.renderer; - - QStringList attributeNames; - - //add attributes needed by the diagram renderer - QList att = diagRenderer->diagramAttributes(); - QList::const_iterator attIt = att.constBegin(); - for ( ; attIt != att.constEnd(); ++attIt ) - { - QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, context.expressionContext() ); - QStringList columns = expression->referencedColumns(); - QStringList::const_iterator columnsIterator = columns.constBegin(); - for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) - { - if ( !attributeNames.contains( *columnsIterator ) ) - attributeNames << *columnsIterator; - } - } - - const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast( diagRenderer ); - if ( linearlyInterpolatedDiagramRenderer != NULL ) + if ( !mSource ) { - if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() ) - { - QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), context.expressionContext() ); - QStringList columns = expression->referencedColumns(); - QStringList::const_iterator columnsIterator = columns.constBegin(); - for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) - { - if ( !attributeNames.contains( *columnsIterator ) ) - attributeNames << *columnsIterator; - } - } - else - { - QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name(); - if ( !attributeNames.contains( name ) ) - attributeNames << name; - } + // we have created the provider with "own feature loop" == false + // so it is assumed that prepare() has been already called followed by registerFeature() calls + return mFeatures; } - //and the ones needed for data defined diagram positions - if ( mSettings.xPosColumn != -1 ) - attributeNames << mFields.at( mSettings.xPosColumn ).name(); - if ( mSettings.yPosColumn != -1 ) - attributeNames << mFields.at( mSettings.yPosColumn ).name(); + QStringList attributeNames; + if ( !prepare( context, attributeNames ) ) + return QList(); QgsRectangle layerExtent = context.extent(); if ( mSettings.ct ) @@ -152,19 +103,17 @@ QList QgsVectorLayerDiagramProvider::labelFeatures( const QgsM request.setSubsetOfAttributes( attributeNames, mFields ); QgsFeatureIterator fit = mSource->getFeatures( request ); - QList features; QgsFeature fet; while ( fit.nextFeature( fet ) ) { - QgsLabelFeature* label = registerDiagram( fet, mapSettings, context ); - if ( label ) - features << label; + registerFeature( fet, context ); } - return features; + return mFeatures; } + void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const { #if 1 // XXX strk @@ -210,8 +159,88 @@ void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext& context, pal::L } -QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, const QgsMapSettings& mapSettings, const QgsRenderContext& context ) +bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) +{ + QgsDiagramLayerSettings& s2 = mSettings; + const QgsMapSettings& mapSettings = mEngine->mapSettings(); + + s2.ct = 0; + if ( mapSettings.hasCrsTransformEnabled() ) + { + if ( context.coordinateTransform() ) + // this is context for layer rendering - use its CT as it includes correct datum transform + s2.ct = context.coordinateTransform()->clone(); + else + // otherwise fall back to creating our own CT - this one may not have the correct datum transform! + s2.ct = new QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() ); + } + + s2.xform = &mapSettings.mapToPixel(); + + s2.fields = mFields; + + s2.renderer = mDiagRenderer; + + const QgsDiagramRendererV2* diagRenderer = s2.renderer; + + //add attributes needed by the diagram renderer + QList att = diagRenderer->diagramAttributes(); + QList::const_iterator attIt = att.constBegin(); + for ( ; attIt != att.constEnd(); ++attIt ) + { + QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, context.expressionContext() ); + QStringList columns = expression->referencedColumns(); + QStringList::const_iterator columnsIterator = columns.constBegin(); + for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) + { + if ( !attributeNames.contains( *columnsIterator ) ) + attributeNames << *columnsIterator; + } + } + + const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast( diagRenderer ); + if ( linearlyInterpolatedDiagramRenderer != NULL ) + { + if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() ) + { + QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), context.expressionContext() ); + QStringList columns = expression->referencedColumns(); + QStringList::const_iterator columnsIterator = columns.constBegin(); + for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) + { + if ( !attributeNames.contains( *columnsIterator ) ) + attributeNames << *columnsIterator; + } + } + else + { + QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name(); + if ( !attributeNames.contains( name ) ) + attributeNames << name; + } + } + + //and the ones needed for data defined diagram positions + if ( mSettings.xPosColumn != -1 ) + attributeNames << mFields.at( mSettings.xPosColumn ).name(); + if ( mSettings.yPosColumn != -1 ) + attributeNames << mFields.at( mSettings.yPosColumn ).name(); + + return true; +} + + +void QgsVectorLayerDiagramProvider::registerFeature( QgsFeature& feature, const QgsRenderContext& context ) +{ + QgsLabelFeature* label = registerDiagram( feature, context ); + if ( label ) + mFeatures << label; +} + + +QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, const QgsRenderContext& context ) { + const QgsMapSettings& mapSettings = mEngine->mapSettings(); QgsDiagramRendererV2* dr = mSettings.renderer; if ( dr ) diff --git a/src/core/qgsvectorlayerdiagramprovider.h b/src/core/qgsvectorlayerdiagramprovider.h index 82f10046f652..a2051b8c3502 100644 --- a/src/core/qgsvectorlayerdiagramprovider.h +++ b/src/core/qgsvectorlayerdiagramprovider.h @@ -33,7 +33,7 @@ class CORE_EXPORT QgsVectorLayerDiagramProvider : public QgsAbstractLabelProvide public: //! Convenience constructor to initialize the provider from given vector layer - explicit QgsVectorLayerDiagramProvider( QgsVectorLayer* layer ); + explicit QgsVectorLayerDiagramProvider( QgsVectorLayer* layer, bool ownFeatureLoop = true ); QgsVectorLayerDiagramProvider( const QgsDiagramLayerSettings* diagSettings, const QgsDiagramRendererV2* diagRenderer, @@ -47,13 +47,19 @@ class CORE_EXPORT QgsVectorLayerDiagramProvider : public QgsAbstractLabelProvide virtual QString id() const override; - virtual QList labelFeatures( const QgsMapSettings& mapSettings, const QgsRenderContext& context ) override; + virtual QList labelFeatures( const QgsRenderContext& context ) override; virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override; + // new virtual methods + + virtual bool prepare( const QgsRenderContext& context, QStringList& attributeNames ); + + virtual void registerFeature( QgsFeature& feature, const QgsRenderContext& context ); + protected: void init(); - QgsLabelFeature* registerDiagram( QgsFeature& feat, const QgsMapSettings& mapSettings, const QgsRenderContext& context ); + QgsLabelFeature* registerDiagram( QgsFeature& feat, const QgsRenderContext& context ); protected: @@ -66,6 +72,7 @@ class CORE_EXPORT QgsVectorLayerDiagramProvider : public QgsAbstractLabelProvide QgsAbstractFeatureSource* mSource; bool mOwnsSource; + QList mFeatures; }; #endif // QGSVECTORLAYERDIAGRAMPROVIDER_H diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp index 71aa0bbe6336..323827816060 100644 --- a/src/core/qgsvectorlayerlabelprovider.cpp +++ b/src/core/qgsvectorlayerlabelprovider.cpp @@ -45,7 +45,7 @@ static void _fixQPictureDPI( QPainter* p ) typedef QgsPalLayerSettings QgsVectorLayerLabelSettings; -QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsVectorLayer* layer ) +QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsVectorLayer* layer, bool withFeatureLoop ) { if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) || !layer->labelsEnabled() ) return; @@ -54,7 +54,16 @@ QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsVectorLayer* layer mLayerId = layer->id(); mFields = layer->fields(); mCrs = layer->crs(); - mSource = new QgsVectorLayerFeatureSource( layer ); + if ( withFeatureLoop ) + { + mSource = new QgsVectorLayerFeatureSource( layer ); + mOwnsSource = true; + } + else + { + mSource = 0; + mOwnsSource = false; + } init(); } @@ -97,7 +106,19 @@ void QgsVectorLayerLabelProvider::init() QgsVectorLayerLabelProvider::~QgsVectorLayerLabelProvider() { + // delete all QgsDataDefined objects (which also deletes their QgsExpression object) + QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::iterator it = mSettings.dataDefinedProperties.begin(); + for ( ; it != mSettings.dataDefinedProperties.constEnd(); ++it ) + { + delete( it.value() ); + it.value() = 0; + } + mSettings.dataDefinedProperties.clear(); + qDeleteAll( mSettings.geometries ); + + if ( mOwnsSource ) + delete mSource; } QString QgsVectorLayerLabelProvider::id() const @@ -105,10 +126,10 @@ QString QgsVectorLayerLabelProvider::id() const return mLayerId; } - -QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMapSettings& mapSettings, const QgsRenderContext& ctx ) +bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) { QgsVectorLayerLabelSettings& lyr = mSettings; + const QgsMapSettings& mapSettings = mEngine->mapSettings(); QgsDebugMsgLevel( "PREPARE LAYER " + mLayerId, 4 ); @@ -116,7 +137,7 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap { if ( lyr.fieldName.isEmpty() ) { - return QList(); + return false; } if ( lyr.isExpression ) @@ -125,7 +146,7 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap if ( exp.hasEvalError() ) { QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 ); - return QList(); + return false; } } else @@ -133,13 +154,11 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap // If we aren't an expression, we check to see if we can find the column. if ( mFields.fieldNameIndex( lyr.fieldName ) == -1 ) { - return QList(); + return false; } } } - QStringList attrNames; - lyr.mCurFields = mFields; if ( lyr.drawLabels ) @@ -149,7 +168,7 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap { // prepare expression for use in QgsPalLayerSettings::registerFeature() QgsExpression* exp = lyr.getLabelExpression(); - exp->prepare( &ctx.expressionContext() ); + exp->prepare( &context.expressionContext() ); if ( exp->hasEvalError() ) { QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 ); @@ -157,12 +176,12 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap Q_FOREACH ( const QString& name, exp->referencedColumns() ) { QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 ); - attrNames.append( name ); + attributeNames.append( name ); } } else { - attrNames.append( lyr.fieldName ); + attributeNames.append( lyr.fieldName ); } // add field indices of data defined expression or field @@ -179,26 +198,29 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap // store parameters for data defined expressions QMap exprParams; - exprParams.insert( "scale", ctx.rendererScale() ); + exprParams.insert( "scale", context.rendererScale() ); dd->setExpressionParams( exprParams ); // this will return columns for expressions or field name, depending upon what is set to be used - QStringList cols = dd->referencedColumns( ctx.expressionContext() ); // <-- prepares any expressions, too + QStringList cols = dd->referencedColumns( context.expressionContext() ); // <-- prepares any expressions, too //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 ); Q_FOREACH ( const QString& name, cols ) { - attrNames.append( name ); + attributeNames.append( name ); } } } // NOW INITIALIZE QgsPalLayerSettings + // TODO: ideally these (non-configuration) members should get out of QgsPalLayerSettings to here + // (together with registerFeature() & related methods) and QgsPalLayerSettings just stores config + //raster and vector scale factors - lyr.vectorScaleFactor = ctx.scaleFactor(); - lyr.rasterCompressFactor = ctx.rasterScaleFactor(); + lyr.vectorScaleFactor = context.scaleFactor(); + lyr.rasterCompressFactor = context.rasterScaleFactor(); // save the pal layer to our layer context (with some additional info) lyr.fieldIndex = mFields.fieldNameIndex( lyr.fieldName ); @@ -206,7 +228,14 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap lyr.xform = &mapSettings.mapToPixel(); lyr.ct = 0; if ( mapSettings.hasCrsTransformEnabled() ) - lyr.ct = new QgsCoordinateTransform( mCrs, mapSettings.destinationCrs() ); + { + if ( context.coordinateTransform() ) + // this is context for layer rendering - use its CT as it includes correct datum transform + lyr.ct = context.coordinateTransform()->clone(); + else + // otherwise fall back to creating our own CT - this one may not have the correct datum transform! + lyr.ct = new QgsCoordinateTransform( mCrs, mapSettings.destinationCrs() ); + } lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 ); lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 ); @@ -220,30 +249,53 @@ QList QgsVectorLayerLabelProvider::labelFeatures( const QgsMap lyr.mFeatsSendingToPal = 0; + return true; +} + + + +QList QgsVectorLayerLabelProvider::labelFeatures( const QgsRenderContext& ctx ) +{ + if ( !mSource ) + { + // we have created the provider with "own feature loop" == false + // so it is assumed that prepare() has been already called followed by registerFeature() calls + return mLabels; + } + + QStringList attrNames; + if ( !prepare( ctx, attrNames ) ) + return QList(); + QgsRectangle layerExtent = ctx.extent(); - if ( lyr.ct ) - layerExtent = lyr.ct->transformBoundingBox( ctx.extent(), QgsCoordinateTransform::ReverseTransform ); + if ( mSettings.ct ) + layerExtent = mSettings.ct->transformBoundingBox( ctx.extent(), QgsCoordinateTransform::ReverseTransform ); QgsFeatureRequest request; request.setFilterRect( layerExtent ); request.setSubsetOfAttributes( attrNames, mFields ); QgsFeatureIterator fit = mSource->getFeatures( request ); - QList labels; - QgsFeature fet; while ( fit.nextFeature( fet ) ) { - QgsLabelFeature* label = 0; - lyr.registerFeature( fet, ctx, QString(), &label ); - if ( label ) - labels << label; + registerFeature( fet, ctx ); } - return labels; + return mLabels; } +void QgsVectorLayerLabelProvider::registerFeature( QgsFeature& feature, const QgsRenderContext& context ) +{ + QgsLabelFeature* label = 0; + mSettings.registerFeature( feature, context, QString(), &label ); + if ( label ) + mLabels << label; +} + + + void QgsVectorLayerLabelProvider::drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const { if ( !mSettings.drawLabels ) @@ -303,7 +355,7 @@ void QgsVectorLayerLabelProvider::drawLabel( QgsRenderContext& context, pal::Lab // update tmpLyr with any data defined drop shadow values QgsPalLabeling::dataDefinedDropShadow( tmpLyr, ddValues ); - tmpLyr.showingShadowRects = mEngine->flags().testFlag( QgsLabelingEngineV2::DrawShadowRects ); + tmpLyr.showingShadowRects = mEngine->testFlag( QgsLabelingEngineV2::DrawShadowRects ); // Render the components of a label in reverse order // (backgrounds -> text) @@ -367,7 +419,7 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition* label, Q component.setOrigin( outPt ); component.setRotation( label->getAlpha() ); - if ( mEngine->flags().testFlag( QgsLabelingEngineV2::DrawLabelRectOnly ) ) // TODO: this should get directly to labeling engine + if ( mEngine->testFlag( QgsLabelingEngineV2::DrawLabelRectOnly ) ) // TODO: this should get directly to labeling engine { //debugging rect if ( drawType != QgsPalLabeling::LabelText ) @@ -602,7 +654,7 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition* label, Q // scale for any print output or image saving @ specific dpi painter->scale( component.dpiRatio(), component.dpiRatio() ); - if ( mEngine->flags().testFlag( QgsLabelingEngineV2::RenderOutlineLabels ) ) + if ( mEngine->testFlag( QgsLabelingEngineV2::RenderOutlineLabels ) ) { // draw outlined text _fixQPictureDPI( painter ); diff --git a/src/core/qgsvectorlayerlabelprovider.h b/src/core/qgsvectorlayerlabelprovider.h index 030dd88e21de..21d3d108299d 100644 --- a/src/core/qgsvectorlayerlabelprovider.h +++ b/src/core/qgsvectorlayerlabelprovider.h @@ -32,7 +32,7 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider public: //! Convenience constructor to initialize the provider from given vector layer - explicit QgsVectorLayerLabelProvider( QgsVectorLayer* layer ); + explicit QgsVectorLayerLabelProvider( QgsVectorLayer* layer, bool withFeatureLoop = true ); QgsVectorLayerLabelProvider( const QgsPalLayerSettings& settings, const QString& layerId, @@ -45,10 +45,28 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider virtual QString id() const override; - virtual QList labelFeatures( const QgsMapSettings& mapSettings, const QgsRenderContext& context ) override; + virtual QList labelFeatures( const QgsRenderContext& context ) override; virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override; + // new virtual methods + + /** + * Prepare for registration of features. Must be called after provider has been added to engine (uses its map settings) + * @param context render context. + * @param attributeNames list of attribute names to which additional required attributes shall be added + * @return List of attributes necessary for labeling + */ + virtual bool prepare( const QgsRenderContext& context, QStringList& attributeNames ); + + /** + * Register a feature for labeling as one or more QgsLabelFeature objects stored into mLabels + * + * @param feature feature to label + * @param context render context. The QgsExpressionContext contained within the render context + * must have already had the feature and fields sets prior to calling this method. + */ + virtual void registerFeature( QgsFeature& feature, const QgsRenderContext& context ); protected: void init(); @@ -57,10 +75,13 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider protected: QgsPalLayerSettings mSettings; QString mLayerId; + // these are needed only if using own renderer loop QgsFields mFields; QgsCoordinateReferenceSystem mCrs; QgsAbstractFeatureSource* mSource; bool mOwnsSource; + + QList mLabels; }; diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index f3121f4830f6..b8374276c55c 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -47,6 +47,8 @@ QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer* layer, QgsRender , mCache( 0 ) , mLabeling( false ) , mDiagrams( false ) + , mLabelProvider( 0 ) + , mDiagramProvider( 0 ) , mLayerTransparency( 0 ) { mSource = new QgsVectorLayerFeatureSource( layer ); @@ -312,6 +314,18 @@ void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit ) mContext.labelingEngine()->registerDiagramFeature( mLayerID, fet, mContext ); } } + // new labeling engine + if ( rendered && mContext.labelingEngineV2() ) + { + if ( mLabelProvider ) + { + mLabelProvider->registerFeature( fet, mContext ); + } + if ( mDiagramProvider ) + { + mDiagramProvider->registerFeature( fet, mContext ); + } + } } catch ( const QgsCsException &cse ) { @@ -469,9 +483,15 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList if ( QgsLabelingEngineV2* engine2 = mContext.labelingEngineV2() ) { if ( layer->labelsEnabled() ) - engine2->addProvider( new QgsVectorLayerLabelProvider( layer ) ); - if ( layer->diagramsEnabled() ) - engine2->addProvider( new QgsVectorLayerDiagramProvider( layer ) ); + { + mLabelProvider = new QgsVectorLayerLabelProvider( layer, false ); + engine2->addProvider( mLabelProvider ); + if ( !mLabelProvider->prepare( mContext, attributeNames ) ) + { + engine2->removeProvider( mLabelProvider ); + mLabelProvider = 0; // deleted by engine + } + } } return; } @@ -480,10 +500,9 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList { mLabeling = true; +#if 0 // TODO: limit of labels, font not found QgsPalLayerSettings& palyr = mContext.labelingEngine()->layer( mLayerID ); - Q_UNUSED( palyr ); -#if 0 // TODO: limit of labels, font not found // see if feature count limit is set for labeling if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 ) { @@ -514,58 +533,29 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QStringList& attributeNames ) { if ( !mContext.labelingEngine() ) + { + if ( QgsLabelingEngineV2* engine2 = mContext.labelingEngineV2() ) + { + if ( layer->diagramsEnabled() ) + { + mDiagramProvider = new QgsVectorLayerDiagramProvider( layer ); + // need to be added before calling prepare() - uses map settings from engine + engine2->addProvider( mDiagramProvider ); + if ( !mDiagramProvider->prepare( mContext, attributeNames ) ) + { + engine2->removeProvider( mDiagramProvider ); + mDiagramProvider = 0; // deleted by engine + } + } + } return; + } if ( !layer->diagramsEnabled() ) return; mDiagrams = true; - const QgsDiagramRendererV2* diagRenderer = layer->diagramRenderer(); - const QgsDiagramLayerSettings* diagSettings = layer->diagramLayerSettings(); - - mContext.labelingEngine()->addDiagramLayer( layer, diagSettings ); // will make internal copy of diagSettings + initialize it - - //add attributes needed by the diagram renderer - QList att = diagRenderer->diagramAttributes(); - QList::const_iterator attIt = att.constBegin(); - for ( ; attIt != att.constEnd(); ++attIt ) - { - QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, mContext.expressionContext() ); - QStringList columns = expression->referencedColumns(); - QStringList::const_iterator columnsIterator = columns.constBegin(); - for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) - { - if ( !attributeNames.contains( *columnsIterator ) ) - attributeNames << *columnsIterator; - } - } - - const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast( layer->diagramRenderer() ); - if ( linearlyInterpolatedDiagramRenderer != NULL ) - { - if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() ) - { - QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), mContext.expressionContext() ); - QStringList columns = expression->referencedColumns(); - QStringList::const_iterator columnsIterator = columns.constBegin(); - for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) - { - if ( !attributeNames.contains( *columnsIterator ) ) - attributeNames << *columnsIterator; - } - } - else - { - QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name(); - if ( !attributeNames.contains( name ) ) - attributeNames << name; - } - } + mContext.labelingEngine()->prepareDiagramLayer( layer, attributeNames, mContext ); // will make internal copy of diagSettings + initialize it - //and the ones needed for data defined diagram positions - if ( diagSettings->xPosColumn != -1 ) - attributeNames << mFields.at( diagSettings->xPosColumn ).name(); - if ( diagSettings->yPosColumn != -1 ) - attributeNames << mFields.at( diagSettings->yPosColumn ).name(); } diff --git a/src/core/qgsvectorlayerrenderer.h b/src/core/qgsvectorlayerrenderer.h index c2a0b7f7022a..535a89f95adb 100644 --- a/src/core/qgsvectorlayerrenderer.h +++ b/src/core/qgsvectorlayerrenderer.h @@ -41,6 +41,8 @@ typedef QList QgsAttributeList; #include "qgsmaplayerrenderer.h" +class QgsVectorLayerLabelProvider; +class QgsVectorLayerDiagramProvider; /** * Implementation of threaded rendering for vector layers. @@ -102,9 +104,18 @@ class QgsVectorLayerRenderer : public QgsMapLayerRenderer QStringList mAttrNames; + //! used with old labeling engine (QgsPalLabeling): whether labeling is enabled bool mLabeling; + //! used with new labeling engine (QgsPalLabeling): whether diagrams are enabled bool mDiagrams; + //! used with new labeling engine (QgsLabelingEngineV2): provider for labels. + //! may be null. no need to delete: if exists it is owned by labeling engine + QgsVectorLayerLabelProvider* mLabelProvider; + //! used with new labeling engine (QgsLabelingEngineV2): provider for diagrams. + //! may be null. no need to delete: if exists it is owned by labeling engine + QgsVectorLayerDiagramProvider* mDiagramProvider; + int mLayerTransparency; QPainter::CompositionMode mFeatureBlendMode; diff --git a/tests/src/core/testqgslabelingenginev2.cpp b/tests/src/core/testqgslabelingenginev2.cpp index d9f98b383bdf..3039128efb7f 100644 --- a/tests/src/core/testqgslabelingenginev2.cpp +++ b/tests/src/core/testqgslabelingenginev2.cpp @@ -77,7 +77,8 @@ void TestQgsLabelingEngineV2::testBasic() vl->setCustomProperty( "labeling/enabled", true ); vl->setCustomProperty( "labeling/fieldName", "Class" ); - QgsLabelingEngineV2 engine( mapSettings ); + QgsLabelingEngineV2 engine; + engine.setMapSettings( mapSettings ); engine.addProvider( new QgsVectorLayerLabelProvider( vl ) ); //engine.setFlags( QgsLabelingEngineV2::RenderOutlineLabels | QgsLabelingEngineV2::DrawLabelRectOnly ); engine.run( context ); @@ -87,6 +88,12 @@ void TestQgsLabelingEngineV2::testBasic() // TODO: replace with render checker img.save( "/tmp/tstlabels.png" ); + // now let's test the variant when integrated into rendering loop + job.start(); + job.waitForFinished(); + QImage img2 = job.renderedImage(); + img2.save( "/tmp/tstlabels2.png" ); + vl->setCustomProperty( "labeling/enabled", false ); } @@ -112,7 +119,8 @@ void TestQgsLabelingEngineV2::testDiagrams() vl->loadNamedStyle( QString( TEST_DATA_DIR ) + "/points_diagrams.qml", res ); Q_ASSERT( res ); - QgsLabelingEngineV2 engine( mapSettings ); + QgsLabelingEngineV2 engine; + engine.setMapSettings( mapSettings ); engine.addProvider( new QgsVectorLayerDiagramProvider( vl ) ); engine.run( context ); @@ -120,6 +128,12 @@ void TestQgsLabelingEngineV2::testDiagrams() // TODO: replace with render checker img.save( "/tmp/tstdiagrams.png" ); + + // now let's test the variant when integrated into rendering loop + job.start(); + job.waitForFinished(); + QImage img2 = job.renderedImage(); + img2.save( "/tmp/tstdiagrams2.png" ); } QTEST_MAIN( TestQgsLabelingEngineV2 )