diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fed6da14b03..10a62776d82 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -163,6 +163,7 @@ SET(QGIS_CORE_SRCS qgsrenderchecker.cpp qgsrendercontext.cpp qgsrectangle.cpp + qgsrulebasedlabeling.cpp qgsrunprocess.cpp qgsscalecalculator.cpp qgsscaleexpression.cpp @@ -186,6 +187,7 @@ SET(QGIS_CORE_SRCS qgsvectorlayerfeatureiterator.cpp qgsvectorlayerimport.cpp qgsvectorlayerjoinbuffer.cpp + qgsvectorlayerlabeling.cpp qgsvectorlayerlabelprovider.cpp qgsvectorlayerrenderer.cpp qgsvectorlayerundocommand.cpp diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index e80c49e92a8..cc955dad272 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -388,11 +388,6 @@ namespace pal } } - QString LabelPosition::getLayerName() const - { - return feature->layer()->name(); - } - void LabelPosition::setConflictsWithObstacle( bool conflicts ) { mHasObstacleConflict = conflicts; diff --git a/src/core/pal/labelposition.h b/src/core/pal/labelposition.h index 3104d1bf6c7..84873373674 100644 --- a/src/core/pal/labelposition.h +++ b/src/core/pal/labelposition.h @@ -164,9 +164,6 @@ namespace pal if ( nextPart ) nextPart->setProblemIds( probFid, lpId ); } - /** Return pointer to layer's name. used for stats */ - QString getLayerName() const; - /** Returns the candidate label position's geographical cost. * @see setCost */ diff --git a/src/core/pal/layer.cpp b/src/core/pal/layer.cpp index 8cf8d435a74..e6c18762d46 100644 --- a/src/core/pal/layer.cpp +++ b/src/core/pal/layer.cpp @@ -45,8 +45,9 @@ namespace pal { - Layer::Layer( const QString &lyrName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll ) - : mName( lyrName ) + Layer::Layer( QgsAbstractLabelProvider* provider, const QString& name, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll ) + : mProvider( provider ) + , mName( name ) , pal( pal ) , mObstacleType( PolygonInterior ) , mActive( active ) diff --git a/src/core/pal/layer.h b/src/core/pal/layer.h index 16198137cef..7029ad5fccd 100644 --- a/src/core/pal/layer.h +++ b/src/core/pal/layer.h @@ -81,6 +81,9 @@ namespace pal */ int featureCount() { return mHashtable.size(); } + /** Returns pointer to the associated provider */ + QgsAbstractLabelProvider* provider() const { return mProvider; } + /** Returns the layer's name. */ QString name() const { return mName; } @@ -242,7 +245,8 @@ namespace pal void chopFeaturesAtRepeatDistance(); protected: - QString mName; /* unique */ + QgsAbstractLabelProvider* mProvider; // not owned + QString mName; /** List of feature parts */ QLinkedList mFeatureParts; @@ -279,7 +283,8 @@ namespace pal /** * \brief Create a new layer * - * @param lyrName layer's name + * @param provider Associated provider + * @param name Name of the layer (for stats, debugging - does not need to be unique) * @param arrangement Arrangement mode : how to place candidates * @param defaultPriority layer's prioriry (0 is the best, 1 the worst) * @param active is the layer is active (currently displayed) @@ -288,7 +293,7 @@ namespace pal * @param displayAll if true, all features will be labelled even though overlaps occur * */ - Layer( const QString& lyrName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll = false ); + Layer( QgsAbstractLabelProvider* provider, const QString& name, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll = false ); /** Add newly created feature part into r tree and to the list */ void addFeaturePart( FeaturePart* fpart, const QString &labelText = QString() ); diff --git a/src/core/pal/pal.cpp b/src/core/pal/pal.cpp index 7d0aa34bf5a..490d04e4206 100644 --- a/src/core/pal/pal.cpp +++ b/src/core/pal/pal.cpp @@ -90,38 +90,17 @@ namespace pal } - QList Pal::getLayers() - { - // TODO make const ! or whatever else - return mLayers.values(); - } - - Layer *Pal::getLayer( const QString& layerName ) - { - mMutex.lock(); - if ( !mLayers.contains( layerName ) ) - { - mMutex.unlock(); - throw new PalException::UnknownLayer(); - } - - Layer* result = mLayers.value( layerName ); - mMutex.unlock(); - return result; - } - void Pal::removeLayer( Layer *layer ) { if ( !layer ) return; mMutex.lock(); - QString key = mLayers.key( layer, QString() ); - if ( !key.isEmpty() ) + if ( QgsAbstractLabelProvider* key = mLayers.key( layer, 0 ) ) { mLayers.remove( key ); + delete layer; } - delete layer; mMutex.unlock(); } @@ -138,21 +117,14 @@ namespace pal //finishGEOS(); } - Layer* Pal::addLayer( const QString &layerName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll ) + Layer* Pal::addLayer( QgsAbstractLabelProvider* provider, const QString& layerName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll ) { mMutex.lock(); - //check if layer is already known - if ( mLayers.contains( layerName ) ) - { - mMutex.unlock(); - //There is already a layer with this name, so we just return the existing one. - //Sometimes the same layer is added twice (e.g. datetime split with otf-reprojection) - return mLayers.value( layerName ); - } + Q_ASSERT( !mLayers.contains( provider ) ); - Layer* layer = new Layer( layerName, arrangement, defaultPriority, active, toLabel, this, displayAll ); - mLayers.insert( layerName, layer ); + Layer* layer = new Layer( provider, layerName, arrangement, defaultPriority, active, toLabel, this, displayAll ); + mLayers.insert( provider, layer ); mMutex.unlock(); return layer; @@ -266,7 +238,7 @@ namespace pal return true; } - Problem* Pal::extract( const QStringList& layerNames, double lambda_min, double phi_min, double lambda_max, double phi_max ) + Problem* Pal::extract( double lambda_min, double phi_min, double lambda_max, double phi_max ) { // to store obstacles RTree *obstacles = new RTree(); @@ -308,14 +280,12 @@ namespace pal // first step : extract features from layers int previousFeatureCount = 0; - Layer *layer; QStringList layersWithFeaturesInBBox; mMutex.lock(); - Q_FOREACH ( const QString& layerName, layerNames ) + Q_FOREACH ( Layer* layer, mLayers.values() ) { - layer = mLayers.value( layerName, 0 ); if ( !layer ) { // invalid layer name @@ -503,15 +473,10 @@ namespace pal return prob; } - std::list* Pal::labeller( double bbox[4], PalStat **stats, bool displayAll ) - { - return labeller( mLayers.keys(), bbox, stats, displayAll ); - } - /* * BIG MACHINE */ - std::list* Pal::labeller( const QStringList& layerNames, double bbox[4], PalStat **stats, bool displayAll ) + std::list* Pal::labeller( double bbox[4], PalStat **stats, bool displayAll ) { #ifdef _DEBUG_ std::cout << "LABELLER (selection)" << std::endl; @@ -536,7 +501,7 @@ namespace pal t.start(); // First, extract the problem - if (( prob = extract( layerNames, bbox[0], bbox[1], bbox[2], bbox[3] ) ) == NULL ) + if (( prob = extract( bbox[0], bbox[1], bbox[2], bbox[3] ) ) == NULL ) { // nothing to be done => return an empty result set if ( stats ) @@ -611,7 +576,7 @@ namespace pal Problem* Pal::extractProblem( double bbox[4] ) { - return extract( mLayers.keys(), bbox[0], bbox[1], bbox[2], bbox[3] ); + return extract( bbox[0], bbox[1], bbox[2], bbox[3] ); } std::list* Pal::solveProblem( Problem* prob, bool displayAll ) diff --git a/src/core/pal/pal.h b/src/core/pal/pal.h index 4871ae6a525..bdfacfaaa9a 100644 --- a/src/core/pal/pal.h +++ b/src/core/pal/pal.h @@ -39,6 +39,8 @@ // TODO ${MAJOR} ${MINOR} etc instead of 0.2 +class QgsAbstractLabelProvider; + /** * * \section intro_sec Introduction @@ -127,6 +129,7 @@ namespace pal /** * \brief add a new layer * + * @param provider Provider associated with the layer * @param layerName layer's name * @param arrangement Howto place candidates * @param defaultPriority layer's prioriry (0 is the best, 1 the worst) @@ -138,25 +141,7 @@ namespace pal * * @todo add symbolUnit */ - Layer* addLayer( const QString& layerName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll = false ); - - /** - * \brief Look for a layer - * - * @param layerName name of layer to search - * - * @throws PalException::UnkownLayer - * - * @return a pointer on layer or NULL if layer not exist - */ - Layer *getLayer( const QString &layerName ); - - /** - * \brief get all layers - * - * @return a list of all layers - */ - QList getLayers(); + Layer* addLayer( QgsAbstractLabelProvider* provider, const QString& layerName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll = false ); /** * \brief remove a layer @@ -177,24 +162,6 @@ namespace pal */ std::list *labeller( double bbox[4], PalStat **stats, bool displayAll ); - /** - * \brief the labeling machine - * Active layers are specifiend through layersName array - * @todo add obstacles and tolabel arrays - * @param layerNames names of layers to label - * @param bbox map extent - * @param stat will be filled with labelling process statistics, can be NULL - * @param displayAll if true, all feature will be labelled even though overlaps occur - * - * @todo UnknownLayer will be ignored ? should throw exception or not ??? - * - * @return A list of label to display on map - */ - std::list *labeller( const QStringList& layerNames, - double bbox[4], - PalStat **stat, - bool displayAll ); - typedef bool ( *FnIsCancelled )( void* ctx ); /** Register a function that returns whether this job has been cancelled - PAL calls it during the computation */ @@ -279,7 +246,7 @@ namespace pal private: - QHash< QString, Layer* > mLayers; + QHash< QgsAbstractLabelProvider*, Layer* > mLayers; QMutex mMutex; @@ -326,14 +293,12 @@ namespace pal * \brief Problem factory * Extract features to label and generates candidates for them, * respects to a bounding box - * @param layersName layers name to be extracted * @param lambda_min xMin bounding-box * @param phi_min yMin bounding-box * @param lambda_max xMax bounding-box * @param phi_max yMax bounding-box */ - Problem* extract( const QStringList& layersName, - double lambda_min, double phi_min, + Problem* extract( double lambda_min, double phi_min, double lambda_max, double phi_max ); diff --git a/src/core/pal/problem.cpp b/src/core/pal/problem.cpp index 1d150e7b94e..ff6bd80353e 100644 --- a/src/core/pal/problem.cpp +++ b/src/core/pal/problem.cpp @@ -2674,7 +2674,7 @@ namespace pal int k; for ( i = 0; i < nbft; i++ ) { - lyrName = mLabelPositions.at( featStartId[i] )->getLayerName(); + lyrName = mLabelPositions.at( featStartId[i] )->getFeaturePart()->feature()->provider()->name(); k = -1; for ( j = 0; j < stats->nbLayers; j++ ) { diff --git a/src/core/qgslabelingenginev2.cpp b/src/core/qgslabelingenginev2.cpp index e31fc148485..7389c3c5d0d 100644 --- a/src/core/qgslabelingenginev2.cpp +++ b/src/core/qgslabelingenginev2.cpp @@ -48,6 +48,7 @@ QgsLabelingEngineV2::~QgsLabelingEngineV2() { delete mResults; qDeleteAll( mProviders ); + qDeleteAll( mSubProviders ); } void QgsLabelingEngineV2::addProvider( QgsAbstractLabelProvider* provider ) @@ -65,6 +66,95 @@ void QgsLabelingEngineV2::removeProvider( QgsAbstractLabelProvider* provider ) } } +void QgsLabelingEngineV2::processProvider( QgsAbstractLabelProvider* provider, QgsRenderContext& context, pal::Pal& p ) +{ + // how to place the labels + pal::Arrangement arrangement; + switch ( provider->placement() ) + { + case QgsPalLayerSettings::AroundPoint: arrangement = pal::P_POINT; break; + case QgsPalLayerSettings::OverPoint: arrangement = pal::P_POINT_OVER; break; + case QgsPalLayerSettings::Line: arrangement = pal::P_LINE; break; + case QgsPalLayerSettings::Curved: arrangement = pal::P_CURVED; break; + case QgsPalLayerSettings::Horizontal: arrangement = pal::P_HORIZ; break; + case QgsPalLayerSettings::Free: arrangement = pal::P_FREE; break; + default: Q_ASSERT( "unsupported placement" && 0 ); return; + } + + QgsAbstractLabelProvider::Flags flags = provider->flags(); + + // create the pal layer + pal::Layer* l = p.addLayer( provider, + provider->name(), + arrangement, + provider->priority(), + true, + flags.testFlag( QgsAbstractLabelProvider::DrawLabels ), + flags.testFlag( QgsAbstractLabelProvider::DrawAllLabels ) ); + + // extra flags for placement of labels for linestrings + l->setArrangementFlags(( pal::LineArrangementFlags ) provider->linePlacementFlags() ); + + // set label mode (label per feature is the default) + l->setLabelMode( flags.testFlag( QgsAbstractLabelProvider::LabelPerFeaturePart ) ? pal::Layer::LabelPerFeaturePart : pal::Layer::LabelPerFeature ); + + // set whether adjacent lines should be merged + l->setMergeConnectedLines( flags.testFlag( QgsAbstractLabelProvider::MergeConnectedLines ) ); + + // set obstacle type + switch ( provider->obstacleType() ) + { + case QgsPalLayerSettings::PolygonInterior: + l->setObstacleType( pal::PolygonInterior ); + break; + case QgsPalLayerSettings::PolygonBoundary: + l->setObstacleType( pal::PolygonBoundary ); + break; + } + + // set whether location of centroid must be inside of polygons + l->setCentroidInside( flags.testFlag( QgsAbstractLabelProvider::CentroidMustBeInside ) ); + + // set whether labels must fall completely within the polygon + l->setFitInPolygonOnly( flags.testFlag( QgsAbstractLabelProvider::FitInPolygonOnly ) ); + + // set how to show upside-down labels + pal::Layer::UpsideDownLabels upsdnlabels; + switch ( provider->upsidedownLabels() ) + { + case QgsPalLayerSettings::Upright: upsdnlabels = pal::Layer::Upright; break; + case QgsPalLayerSettings::ShowDefined: upsdnlabels = pal::Layer::ShowDefined; break; + case QgsPalLayerSettings::ShowAll: upsdnlabels = pal::Layer::ShowAll; break; + default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return; + } + l->setUpsidedownLabels( upsdnlabels ); + + + QList features = provider->labelFeatures( context ); + + foreach ( QgsLabelFeature* feature, features ) + { + try + { + l->registerFeature( feature ); + } + catch ( std::exception &e ) + { + Q_UNUSED( e ); + QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 ); + continue; + } + } + + // any sub-providers? + Q_FOREACH ( QgsAbstractLabelProvider* subProvider, provider->subProviders() ) + { + mSubProviders << subProvider; + processProvider( subProvider, context, p ); + } +} + + void QgsLabelingEngineV2::run( QgsRenderContext& context ) { pal::Pal p; @@ -92,82 +182,7 @@ void QgsLabelingEngineV2::run( QgsRenderContext& context ) // for each provider: get labels and register them in PAL foreach ( QgsAbstractLabelProvider* provider, mProviders ) { - // how to place the labels - pal::Arrangement arrangement; - switch ( provider->placement() ) - { - case QgsPalLayerSettings::AroundPoint: arrangement = pal::P_POINT; break; - case QgsPalLayerSettings::OverPoint: arrangement = pal::P_POINT_OVER; break; - case QgsPalLayerSettings::Line: arrangement = pal::P_LINE; break; - case QgsPalLayerSettings::Curved: arrangement = pal::P_CURVED; break; - case QgsPalLayerSettings::Horizontal: arrangement = pal::P_HORIZ; break; - case QgsPalLayerSettings::Free: arrangement = pal::P_FREE; break; - default: Q_ASSERT( "unsupported placement" && 0 ); return; - } - - QgsAbstractLabelProvider::Flags flags = provider->flags(); - - // create the pal layer - pal::Layer* l = p.addLayer( provider->id(), - arrangement, - provider->priority(), - true, - flags.testFlag( QgsAbstractLabelProvider::DrawLabels ), - flags.testFlag( QgsAbstractLabelProvider::DrawAllLabels ) ); - - // extra flags for placement of labels for linestrings - l->setArrangementFlags(( pal::LineArrangementFlags ) provider->linePlacementFlags() ); - - // set label mode (label per feature is the default) - l->setLabelMode( flags.testFlag( QgsAbstractLabelProvider::LabelPerFeaturePart ) ? pal::Layer::LabelPerFeaturePart : pal::Layer::LabelPerFeature ); - - // set whether adjacent lines should be merged - l->setMergeConnectedLines( flags.testFlag( QgsAbstractLabelProvider::MergeConnectedLines ) ); - - // set obstacle type - switch ( provider->obstacleType() ) - { - case QgsPalLayerSettings::PolygonInterior: - l->setObstacleType( pal::PolygonInterior ); - break; - case QgsPalLayerSettings::PolygonBoundary: - l->setObstacleType( pal::PolygonBoundary ); - break; - } - - // set whether location of centroid must be inside of polygons - l->setCentroidInside( flags.testFlag( QgsAbstractLabelProvider::CentroidMustBeInside ) ); - - // set whether labels must fall completely within the polygon - l->setFitInPolygonOnly( flags.testFlag( QgsAbstractLabelProvider::FitInPolygonOnly ) ); - - // set how to show upside-down labels - pal::Layer::UpsideDownLabels upsdnlabels; - switch ( provider->upsidedownLabels() ) - { - case QgsPalLayerSettings::Upright: upsdnlabels = pal::Layer::Upright; break; - case QgsPalLayerSettings::ShowDefined: upsdnlabels = pal::Layer::ShowDefined; break; - case QgsPalLayerSettings::ShowAll: upsdnlabels = pal::Layer::ShowAll; break; - default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return; - } - l->setUpsidedownLabels( upsdnlabels ); - - - QList features = provider->labelFeatures( context ); - - foreach ( QgsLabelFeature* feature, features ) - { - try - { - l->registerFeature( feature ); - } - catch ( std::exception &e ) - { - Q_UNUSED( e ); - QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 ); - continue; - } - } + processProvider( provider, context, p ); } @@ -269,10 +284,7 @@ void QgsLabelingEngineV2::run( QgsRenderContext& context ) continue; } - //layer names - QString layerName = ( *it )->getLayerName(); - - providerById( layerName )->drawLabel( context, *it ); + lf->provider()->drawLabel( context, *it ); } // Reset composition mode for further drawing operations @@ -327,16 +339,6 @@ void QgsLabelingEngineV2::writeSettingsToProject() QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", mFlags.testFlag( RenderOutlineLabels ) ); } -QgsAbstractLabelProvider* QgsLabelingEngineV2::providerById( const QString& id ) -{ - Q_FOREACH ( QgsAbstractLabelProvider* provider, mProviders ) - { - if ( provider->id() == id ) - return provider; - } - return 0; -} - //// @@ -369,6 +371,12 @@ QgsLabelFeature::~QgsLabelFeature() delete mInfo; } +QgsAbstractLabelProvider*QgsLabelFeature::provider() const +{ + return mLayer ? mLayer->provider() : 0; + +} + QgsAbstractLabelProvider::QgsAbstractLabelProvider() : mEngine( 0 ) , mFlags( DrawLabels ) diff --git a/src/core/qgslabelingenginev2.h b/src/core/qgslabelingenginev2.h index 47c72bcd617..829a10cc2c3 100644 --- a/src/core/qgslabelingenginev2.h +++ b/src/core/qgslabelingenginev2.h @@ -24,6 +24,7 @@ #include +class QgsAbstractLabelProvider; class QgsRenderContext; class QgsGeometry; @@ -183,6 +184,9 @@ class CORE_EXPORT QgsLabelFeature //! Assign PAL layer to the label feature. Should be only used internally in PAL void setLayer( pal::Layer* layer ) { mLayer = layer; } + //! Return provider of this instance + QgsAbstractLabelProvider* provider() const; + protected: //! Pointer to PAL layer (assigned when registered to PAL) pal::Layer* mLayer; @@ -262,15 +266,18 @@ class CORE_EXPORT QgsAbstractLabelProvider }; Q_DECLARE_FLAGS( Flags, Flag ) - //! Return unique identifier of the provider - virtual QString id() const = 0; - //! Return list of label features (they are owned by the provider and thus deleted on its destruction) 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; + //! Return list of child providers - useful if the provider needs to put labels into more layers with different configuration + virtual QList subProviders() { return QList(); } + + //! Name of the layer (for statistics, debugging etc.) - does not need to be unique + QString name() const { return mName; } + //! Flags associated with the provider Flags flags() const { return mFlags; } @@ -289,11 +296,12 @@ class CORE_EXPORT QgsAbstractLabelProvider //! How to handle labels that would be upside down QgsPalLayerSettings::UpsideDownLabels upsidedownLabels() const { return mUpsidedownLabels; } - protected: //! Associated labeling engine const QgsLabelingEngineV2* mEngine; + //! Name of the layer + QString mName; //! Flags altering drawing and registration of features Flags mFlags; //! Placement strategy @@ -366,9 +374,6 @@ class CORE_EXPORT QgsLabelingEngineV2 //! 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 ); @@ -402,11 +407,15 @@ class CORE_EXPORT QgsLabelingEngineV2 //! Write configuration of the labeling engine to the current project file void writeSettingsToProject(); + protected: + void processProvider( QgsAbstractLabelProvider* provider, QgsRenderContext& context, pal::Pal& p ); + protected: //! Associated map settings instance QgsMapSettings mMapSettings; //! List of providers (the are owned by the labeling engine) QList mProviders; + QList mSubProviders; //! Flags Flags mFlags; //! search method to use for removal collisions between labels diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 672b287d9bc..3ed35c500c8 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -3207,14 +3207,20 @@ void QgsPalLabeling::clearActiveLayer( const QString &layerID ) Q_UNUSED( layerID ); } +#include "qgsvectorlayerlabeling.h" + int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) { - if ( !willUseLayer( layer ) || !layer->labelsEnabled() ) + if ( !willUseLayer( layer ) ) { return 0; } - QgsVectorLayerLabelProvider* lp = new QgsVectorLayerLabelProvider( layer, false ); + QgsVectorLayerLabelProvider* lp = layer->labeling().provider( layer ); + if ( !lp ) + return 0; + + //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 diff --git a/src/core/qgsrulebasedlabeling.cpp b/src/core/qgsrulebasedlabeling.cpp new file mode 100644 index 00000000000..36604d97776 --- /dev/null +++ b/src/core/qgsrulebasedlabeling.cpp @@ -0,0 +1,183 @@ +#include "qgsrulebasedlabeling.h" + + + +QgsRuleBasedLabelProvider::QgsRuleBasedLabelProvider( const QgsRuleBasedLabeling& rules, QgsVectorLayer* layer, bool withFeatureLoop ) + : QgsVectorLayerLabelProvider( layer, withFeatureLoop ) + , mRules( rules ) +{ + mRules.rootRule()->createSubProviders( layer, mSubProviders ); +} + +QgsRuleBasedLabelProvider::~QgsRuleBasedLabelProvider() +{ + // sub-providers owned by labeling engine +} + + +bool QgsRuleBasedLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) +{ + Q_FOREACH ( QgsVectorLayerLabelProvider* provider, mSubProviders.values() ) + provider->setEngine( mEngine ); + + // populate sub-providers + mRules.rootRule()->prepare( context, attributeNames, mSubProviders ); + return true; +} + +void QgsRuleBasedLabelProvider::registerFeature( QgsFeature& feature, const QgsRenderContext& context ) +{ + // will register the feature to relevant sub-providers + mRules.rootRule()->registerFeature( feature, context, mSubProviders ); +} + +QList QgsRuleBasedLabelProvider::subProviders() +{ + QList lst; + Q_FOREACH ( QgsVectorLayerLabelProvider* subprovider, mSubProviders.values() ) + lst << subprovider; + return lst; +} + + +//////////////////// + +QgsRuleBasedLabeling::Rule::Rule( QgsPalLayerSettings* settings, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& label, const QString& description, bool elseRule ) + : mParent( 0 ), mSettings( settings ) + , mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom ) + , mFilterExp( filterExp ), mLabel( label ), mDescription( description ) + , mElseRule( elseRule ) + //, mIsActive( true ) + , mFilter( 0 ) +{ + initFilter(); +} + +QgsRuleBasedLabeling::Rule::~Rule() +{ + delete mSettings; + delete mFilter; + qDeleteAll( mChildren ); + // do NOT delete parent +} + +void QgsRuleBasedLabeling::Rule::initFilter() +{ + if ( mElseRule || mFilterExp.compare( "ELSE", Qt::CaseInsensitive ) == 0 ) + { + mElseRule = true; + mFilter = 0; + } + else if ( !mFilterExp.isEmpty() ) + { + delete mFilter; + mFilter = new QgsExpression( mFilterExp ); + } + else + { + mFilter = 0; + } +} + + +void QgsRuleBasedLabeling::Rule::appendChild( QgsRuleBasedLabeling::Rule* rule ) +{ + mChildren.append( rule ); + rule->mParent = this; + // TODO updateElseRules(); +} + +QgsRuleBasedLabeling::Rule*QgsRuleBasedLabeling::Rule::clone() const +{ + QgsPalLayerSettings* s = mSettings ? new QgsPalLayerSettings( *mSettings ) : 0; + Rule* newrule = new Rule( s, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mLabel, mDescription ); + // TODO newrule->setCheckState( mIsActive ); + // clone children + Q_FOREACH ( Rule* rule, mChildren ) + newrule->appendChild( rule->clone() ); + return newrule; +} + +void QgsRuleBasedLabeling::Rule::createSubProviders( QgsVectorLayer* layer, QgsRuleBasedLabeling::RuleToProviderMap& subProviders ) +{ + if ( mSettings ) + { + // add provider! + QgsVectorLayerLabelProvider* p = new QgsVectorLayerLabelProvider( layer, false, mSettings ); + subProviders[this] = p; + } + + // call recursively + Q_FOREACH ( Rule* rule, mChildren ) + { + rule->createSubProviders( layer, subProviders ); + } +} + +void QgsRuleBasedLabeling::Rule::prepare( const QgsRenderContext& context, QStringList& attributeNames, QgsRuleBasedLabeling::RuleToProviderMap& subProviders ) +{ + if ( mSettings ) + { + QgsVectorLayerLabelProvider* p = subProviders[this]; + if ( !p->prepare( context, attributeNames ) ) + { + subProviders.remove( this ); + delete p; + } + } + + if ( mFilter ) + mFilter->prepare( &context.expressionContext() ); + + // call recursively + Q_FOREACH ( Rule* rule, mChildren ) + { + rule->prepare( context, attributeNames, subProviders ); + } +} + +void QgsRuleBasedLabeling::Rule::registerFeature( QgsFeature& feature, const QgsRenderContext& context, QgsRuleBasedLabeling::RuleToProviderMap& subProviders ) +{ + bool isOK = isFilterOK( feature, const_cast( context ) ); // TODO: remove const_cast + + if ( !isOK ) + return; + + // do we have active subprovider for the rule? + if ( subProviders.contains( this ) ) + subProviders[this]->registerFeature( feature, context ); + + // call recursively + Q_FOREACH ( Rule* rule, mChildren ) + { + rule->registerFeature( feature, context, subProviders ); + } +} + +bool QgsRuleBasedLabeling::Rule::isFilterOK( QgsFeature& f, QgsRenderContext& context ) const +{ + if ( ! mFilter || mElseRule ) + return true; + + context.expressionContext().setFeature( f ); + QVariant res = mFilter->evaluate( &context.expressionContext() ); + return res.toInt() != 0; +} + +//////////////////// + +QgsRuleBasedLabeling::QgsRuleBasedLabeling( QgsRuleBasedLabeling::Rule* root ) + : mRootRule( root ) +{ + +} + +QgsRuleBasedLabeling::QgsRuleBasedLabeling( const QgsRuleBasedLabeling& other ) +{ + mRootRule = other.mRootRule->clone(); +} + +QgsRuleBasedLabeling::~QgsRuleBasedLabeling() +{ + delete mRootRule; +} diff --git a/src/core/qgsrulebasedlabeling.h b/src/core/qgsrulebasedlabeling.h new file mode 100644 index 00000000000..ef8996b92f3 --- /dev/null +++ b/src/core/qgsrulebasedlabeling.h @@ -0,0 +1,189 @@ +#ifndef QGSRULEBASEDLABELING_H +#define QGSRULEBASEDLABELING_H + +#include + +class QgsExpression; +class QgsFeature; +class QgsPalLayerSettings; +class QgsRenderContext; +class QgsVectorLayer; +class QgsVectorLayerLabelProvider; + + +class CORE_EXPORT QgsRuleBasedLabeling +{ + public: + class Rule; + typedef QList RuleList; + typedef QMap RuleToProviderMap; + + class Rule + { + public: + //! takes ownership of settings + Rule( QgsPalLayerSettings* settings, int scaleMinDenom = 0, int scaleMaxDenom = 0, const QString& filterExp = QString(), const QString& label = QString(), const QString& description = QString(), bool elseRule = false ); + ~Rule(); + + QgsPalLayerSettings* settings() const { return mSettings; } + QString label() const { return mLabel; } + bool dependsOnScale() const { return mScaleMinDenom != 0 || mScaleMaxDenom != 0; } + int scaleMinDenom() const { return mScaleMinDenom; } + int scaleMaxDenom() const { return mScaleMaxDenom; } + /** + * A filter that will check if this rule applies + * @return An expression + */ + QString filterExpression() const { return mFilterExp; } + /** + * A human readable description for this rule + * + * @return Description + */ + QString description() const { return mDescription; } + /** + * Check if this rule is an ELSE rule + * + * @return True if this rule is an else rule + */ + bool isElse() { return mElseRule; } + + void setLabel( QString label ) { mLabel = label; } + /** + * Set the minimum denominator for which this rule shall apply. + * E.g. 1000 if it shall be evaluated between 1:1000 and 1:100'000 + * Set to 0 to disable the minimum check + * @param scaleMinDenom The minimum scale denominator for this rule + */ + void setScaleMinDenom( int scaleMinDenom ) { mScaleMinDenom = scaleMinDenom; } + /** + * Set the maximum denominator for which this rule shall apply. + * E.g. 100'000 if it shall be evaluated between 1:1000 and 1:100'000 + * Set to 0 to disable the maximum check + * @param scaleMaxDenom maximum scale denominator for this rule + */ + void setScaleMaxDenom( int scaleMaxDenom ) { mScaleMaxDenom = scaleMaxDenom; } + /** + * Set the expression used to check if a given feature shall be rendered with this rule + * + * @param filterExp An expression + */ + void setFilterExpression( QString filterExp ) { mFilterExp = filterExp; initFilter(); } + /** + * Set a human readable description for this rule + * + * @param description Description + */ + void setDescription( QString description ) { mDescription = description; } + /** + * Sets if this rule is an ELSE rule + * + * @param iselse If true, this rule is an ELSE rule + */ + void setIsElse( bool iselse ) { mElseRule = iselse; } + + + // parent / child operations + + /** + * Return all children rules of this rule + * + * @return A list of rules + */ + RuleList& children() { return mChildren; } + /** + * The parent rule + * + * @return Parent rule + */ + Rule* parent() { return mParent; } + + //! add child rule, take ownership, sets this as parent + void appendChild( Rule* rule ); + + //! clone this rule, return new instance + Rule* clone() const; + + // TODO: load / save + + // evaluation + + //! add providers + void createSubProviders( QgsVectorLayer* layer, RuleToProviderMap& subProviders ); + + //! call prepare() on sub-providers and populate attributeNames + void prepare( const QgsRenderContext& context, QStringList& attributeNames, RuleToProviderMap& subProviders ); + + //! register individual features + void registerFeature( QgsFeature& feature, const QgsRenderContext& context, RuleToProviderMap& subProviders ); + + protected: + /** + * Check if a given feature shall be labelled by this rule + * + * @param f The feature to test + * @param context The context in which the rendering happens + * @return True if the feature shall be rendered + */ + bool isFilterOK( QgsFeature& f, QgsRenderContext& context ) const; + + void initFilter(); + + protected: + Rule* mParent; // parent rule (NULL only for root rule) + QgsPalLayerSettings* mSettings; + int mScaleMinDenom, mScaleMaxDenom; + QString mFilterExp, mLabel, mDescription; + bool mElseRule; + RuleList mChildren; + + // temporary + QgsExpression* mFilter; + }; + + + //! Constructs the labeling from given tree of rules (takes ownership) + explicit QgsRuleBasedLabeling( QgsRuleBasedLabeling::Rule* root ); + //! Copy constructor + QgsRuleBasedLabeling( const QgsRuleBasedLabeling& other ); + ~QgsRuleBasedLabeling(); + + Rule* rootRule() { return mRootRule; } + + // TODO: static create() from DOM + + + + protected: + Rule* mRootRule; +}; + + +#include "qgsvectorlayerlabelprovider.h" + + +class CORE_EXPORT QgsRuleBasedLabelProvider : public QgsVectorLayerLabelProvider +{ + public: + QgsRuleBasedLabelProvider( const QgsRuleBasedLabeling& rules, QgsVectorLayer* layer, bool withFeatureLoop = true ); + ~QgsRuleBasedLabelProvider(); + + // reimplemented + + virtual bool prepare( const QgsRenderContext& context, QStringList& attributeNames ) override; + + virtual void registerFeature( QgsFeature& feature, const QgsRenderContext& context ) override; + + // new methods + + virtual QList subProviders() override; + + protected: + //! owned copy + QgsRuleBasedLabeling mRules; + //! label providers are owned by labeling engine + QgsRuleBasedLabeling::RuleToProviderMap mSubProviders; +}; + + +#endif // QGSRULEBASEDLABELING_H diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index ba64dc17941..adf127fa29e 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -69,6 +69,7 @@ #include "qgsvectorlayereditutils.h" #include "qgsvectorlayerfeatureiterator.h" #include "qgsvectorlayerjoinbuffer.h" +#include "qgsvectorlayerlabeling.h" #include "qgsvectorlayerrenderer.h" #include "qgsvectorlayerundocommand.h" #include "qgspointv2.h" @@ -131,6 +132,7 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath, , mRendererV2( NULL ) , mLabel( 0 ) , mLabelOn( false ) + , mLabeling( new QgsVectorLayerLabeling ) , mLabelFontNotFoundNotified( false ) , mFeatureBlendMode( QPainter::CompositionMode_SourceOver ) // Default to normal feature blending , mLayerTransparency( 0 ) @@ -184,7 +186,8 @@ QgsVectorLayer::~QgsVectorLayer() delete mJoinBuffer; delete mExpressionFieldBuffer; delete mCache; - delete mLabel; + delete mLabel; // old deprecated implementation + delete mLabeling; delete mDiagramLayerSettings; delete mDiagramRenderer; diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 61087f84bbe..2508dba2211 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -63,6 +63,7 @@ class QgsSymbolV2; class QgsVectorDataProvider; class QgsVectorLayerEditBuffer; class QgsVectorLayerJoinBuffer; +class QgsVectorLayerLabeling; class QgsPointV2; typedef QList QgsAttributeList; @@ -1261,6 +1262,11 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ Q_DECL_DEPRECATED bool hasLabelsEnabled() const; + /** Access to labeling configuration + * @note added in 2.12 + */ + QgsVectorLayerLabeling& labeling() { return *mLabeling; } + /** Returns true if the provider is in editing mode */ virtual bool isEditable() const override; @@ -2079,12 +2085,15 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Simplification object which holds the information about how to simplify the features for fast rendering */ QgsVectorSimplifyMethod mSimplifyMethod; - /** Label */ + /** Label [old deprecated implementation] */ QgsLabel *mLabel; - /** Display labels */ + /** Display labels [old deprecated implementation] */ bool mLabelOn; + /** Labeling configuration */ + QgsVectorLayerLabeling* mLabeling; + /** Whether 'labeling font not found' has be shown for this layer (only show once in QgsMessageBar, on first rendering) */ bool mLabelFontNotFoundNotified; diff --git a/src/core/qgsvectorlayerdiagramprovider.cpp b/src/core/qgsvectorlayerdiagramprovider.cpp index 4072d975d11..47987f626f0 100644 --- a/src/core/qgsvectorlayerdiagramprovider.cpp +++ b/src/core/qgsvectorlayerdiagramprovider.cpp @@ -59,6 +59,7 @@ QgsVectorLayerDiagramProvider::QgsVectorLayerDiagramProvider( QgsVectorLayer* la void QgsVectorLayerDiagramProvider::init() { + mName = mLayerId; mPriority = 1 - mSettings.priority / 10.0; // convert 0..10 --> 1..0 mPlacement = QgsPalLayerSettings::Placement( mSettings.placement ); mLinePlacementFlags = mSettings.placementFlags; @@ -77,11 +78,6 @@ QgsVectorLayerDiagramProvider::~QgsVectorLayerDiagramProvider() } -QString QgsVectorLayerDiagramProvider::id() const -{ - return mLayerId + "d"; -} - QList QgsVectorLayerDiagramProvider::labelFeatures( const QgsRenderContext& context ) { if ( !mSource ) @@ -154,10 +150,7 @@ void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext& context, pal::L mSettings.renderer->renderDiagram( feature, context, centerPt.toQPointF() ); //insert into label search tree to manipulate position interactively - //for diagrams, remove the additional 'd' at the end of the layer id - QString layerId = id(); - layerId.chop( 1 ); - mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), layerId, QString(), QFont(), true, false ); + mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false ); } diff --git a/src/core/qgsvectorlayerdiagramprovider.h b/src/core/qgsvectorlayerdiagramprovider.h index f9ace5e7459..6cbc0fa6d02 100644 --- a/src/core/qgsvectorlayerdiagramprovider.h +++ b/src/core/qgsvectorlayerdiagramprovider.h @@ -70,8 +70,6 @@ class CORE_EXPORT QgsVectorLayerDiagramProvider : public QgsAbstractLabelProvide //! Clean up ~QgsVectorLayerDiagramProvider(); - virtual QString id() const override; - virtual QList labelFeatures( const QgsRenderContext& context ) override; virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override; diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp new file mode 100644 index 00000000000..c6966048c86 --- /dev/null +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -0,0 +1,75 @@ +#include "qgsvectorlayerlabeling.h" + +#include "qgspallabeling.h" +#include "qgsrulebasedlabeling.h" +#include "qgsvectorlayer.h" + +QgsVectorLayerLabeling::QgsVectorLayerLabeling() + : mMode( SimpleLabels ) + //, mSimpleLabeling( 0 ) + , mRuleBasedLabeling( 0 ) +{ + +} + +QgsVectorLayerLabeling::~QgsVectorLayerLabeling() +{ + //delete mSimpleLabeling; + delete mRuleBasedLabeling; +} + +//QgsPalLayerSettings QgsVectorLayerLabeling::simpleLabeling() +//{ +//} + +//void QgsVectorLayerLabeling::setSimpleLabeling(QgsPalLayerSettings* settings) +//{ +// delete mSimpleLabeling; +// mSimpleLabeling = settings; +//} + +void QgsVectorLayerLabeling::setRuleBasedLabeling( QgsRuleBasedLabeling* settings ) +{ + delete mRuleBasedLabeling; + mRuleBasedLabeling = settings; +} + +QgsVectorLayerLabelProvider* QgsVectorLayerLabeling::provider( QgsVectorLayer* layer ) +{ + if ( mMode == SimpleLabels ) + { + if ( layer->customProperty( "labeling" ).toString() == QString( "pal" ) && layer->labelsEnabled() ) + return new QgsVectorLayerLabelProvider( layer, false ); + } + else // rule-based + { + if ( mRuleBasedLabeling ) + return new QgsRuleBasedLabelProvider( *mRuleBasedLabeling, layer, false ); + } + return 0; +} + +/* +QgsAbstractLabelProvider* QgsVectorLayerLabeling::addProviderToEngine( QgsVectorLayer* layer, QgsLabelingEngineV2* engine, QgsRenderContext& context ) +{ + if ( mMode == SimpleLabels ) + { + QgsVectorLayerLabelProvider* provider = 0; + if ( layer->labelsEnabled() ) + { + provider = new QgsVectorLayerLabelProvider( layer, false ); + engine->addProvider( provider ); + if ( !provider->prepare( context, attributeNames ) ) + { + engine->removeProvider( provider ); + provider = 0; // deleted by engine + } + } + } + else // rule-based + { + return 0; // TODO + } + +} +*/ diff --git a/src/core/qgsvectorlayerlabeling.h b/src/core/qgsvectorlayerlabeling.h new file mode 100644 index 00000000000..e54f15360ef --- /dev/null +++ b/src/core/qgsvectorlayerlabeling.h @@ -0,0 +1,49 @@ +#ifndef QGSVECTORLAYERLABELING_H +#define QGSVECTORLAYERLABELING_H + + +class QgsPalLayerSettings; +class QgsRuleBasedLabeling; +class QgsVectorLayer; +class QgsVectorLayerLabelProvider; + +class CORE_EXPORT QgsVectorLayerLabeling +{ + public: + enum Mode + { + //NoLabels, //!< the layer does not participate in labeling + //Obstacles, //!< no labels are shown, but layer's features act as obstacles for other labels + SimpleLabels, //!< the layer is labelled with one style + RuleBasedLabels //!< the layer is labelled with multiple styles defined with rules + }; + + //! Defaults to no labels + QgsVectorLayerLabeling(); + ~QgsVectorLayerLabeling(); + + Mode mode() const { return mMode; } + void setMode( Mode m ) { mMode = m; } + + //QgsPalLayerSettings simpleLabeling(); + + //QgsPalLayerSettings* simpleLabeling() const { return mSimpleLabeling; } + //! Assign simple labeling configuration (takes ownership) + //void setSimpleLabeling( QgsPalLayerSettings* settings ); + + QgsRuleBasedLabeling* ruleBasedLabeling() const { return mRuleBasedLabeling; } + //! Assign rule-based labeling configuration (takes ownership) + void setRuleBasedLabeling( QgsRuleBasedLabeling* settings ); + + //! Factory for label provider implementation - according to the current mode + QgsVectorLayerLabelProvider* provider( QgsVectorLayer* layer ); + + protected: + Mode mMode; + //QgsPalLayerSettings* mSimpleLabeling; + QgsRuleBasedLabeling* mRuleBasedLabeling; + +}; + + +#endif // QGSVECTORLAYERLABELING_H diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp index 0dab1f09afd..3a615106784 100644 --- a/src/core/qgsvectorlayerlabelprovider.cpp +++ b/src/core/qgsvectorlayerlabelprovider.cpp @@ -43,14 +43,11 @@ static void _fixQPictureDPI( QPainter* p ) } -typedef QgsPalLayerSettings QgsVectorLayerLabelSettings; -QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsVectorLayer* layer, bool withFeatureLoop ) +QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsVectorLayer* layer, bool withFeatureLoop, const QgsPalLayerSettings* settings, const QString& layerName ) { - if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) || !layer->labelsEnabled() ) - return; - - mSettings = QgsVectorLayerLabelSettings::fromLayer( layer ); + mSettings = settings ? *settings : QgsPalLayerSettings::fromLayer( layer ); + mName = layerName.isEmpty() ? layer->id() : layerName; mLayerId = layer->id(); mFields = layer->fields(); mCrs = layer->crs(); @@ -120,14 +117,10 @@ QgsVectorLayerLabelProvider::~QgsVectorLayerLabelProvider() delete mSource; } -QString QgsVectorLayerLabelProvider::id() const -{ - return mLayerId; -} bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) { - QgsVectorLayerLabelSettings& lyr = mSettings; + QgsPalLayerSettings& lyr = mSettings; const QgsMapSettings& mapSettings = mEngine->mapSettings(); QgsDebugMsgLevel( "PREPARE LAYER " + mLayerId, 4 ); @@ -389,7 +382,7 @@ void QgsVectorLayerLabelProvider::drawLabel( QgsRenderContext& context, pal::Lab // add to the results QString labeltext = label->getFeaturePart()->feature()->labelText(); - mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), id(), labeltext, dFont, false, lf->hasFixedPosition() ); + mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, labeltext, dFont, false, lf->hasFixedPosition() ); } diff --git a/src/core/qgsvectorlayerlabelprovider.h b/src/core/qgsvectorlayerlabelprovider.h index bc333eaa7ea..36c68d63d9b 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, bool withFeatureLoop = true ); + explicit QgsVectorLayerLabelProvider( QgsVectorLayer* layer, bool withFeatureLoop = true, const QgsPalLayerSettings* settings = 0, const QString& layerName = QString() ); //! Construct diagram provider with all the necessary configuration parameters QgsVectorLayerLabelProvider( const QgsPalLayerSettings& settings, @@ -44,8 +44,6 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider ~QgsVectorLayerLabelProvider(); - virtual QString id() const override; - virtual QList labelFeatures( const QgsRenderContext& context ) override; virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override; diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index 854f2bd17df..bbf57750607 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -29,6 +29,7 @@ #include "qgsvectorlayer.h" #include "qgsvectorlayerdiagramprovider.h" #include "qgsvectorlayerfeatureiterator.h" +#include "qgsvectorlayerlabeling.h" #include "qgsvectorlayerlabelprovider.h" #include "qgspainteffect.h" @@ -494,7 +495,19 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList { if ( QgsLabelingEngineV2* engine2 = mContext.labelingEngineV2() ) { - if ( layer->labelsEnabled() ) + mLabelProvider = layer->labeling().provider( layer ); + if ( mLabelProvider ) + { + engine2->addProvider( mLabelProvider ); + if ( !mLabelProvider->prepare( mContext, attributeNames ) ) + { + engine2->removeProvider( mLabelProvider ); + mLabelProvider = 0; // deleted by engine + } + } + + //mLabelProvider = layer->labeling().addProviderToEngine( layer, engine2, mContext ); + /*if ( layer->labelsEnabled() ) { mLabelProvider = new QgsVectorLayerLabelProvider( layer, false ); engine2->addProvider( mLabelProvider ); @@ -503,7 +516,7 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList engine2->removeProvider( mLabelProvider ); mLabelProvider = 0; // deleted by engine } - } + }*/ } return; } diff --git a/tests/src/core/testqgslabelingenginev2.cpp b/tests/src/core/testqgslabelingenginev2.cpp index e51549d5f8d..45ae3c34446 100644 --- a/tests/src/core/testqgslabelingenginev2.cpp +++ b/tests/src/core/testqgslabelingenginev2.cpp @@ -31,6 +31,7 @@ class TestQgsLabelingEngineV2 : public QObject void cleanupTestCase(); void testBasic(); void testDiagrams(); + void testRuleBased(); private: QgsVectorLayer* vl; @@ -135,6 +136,62 @@ void TestQgsLabelingEngineV2::testDiagrams() QImage img2 = job.renderedImage(); QCOMPARE( img, img2 ); + + vl->loadDefaultStyle( res ); +} + +#include "qgsvectorlayerlabeling.h" +#include "qgsrulebasedlabeling.h" + +void TestQgsLabelingEngineV2::testRuleBased() +{ + QSize size( 640, 480 ); + QgsMapSettings mapSettings; + mapSettings.setOutputSize( size ); + mapSettings.setExtent( vl->extent() ); + mapSettings.setLayers( QStringList() << vl->id() ); + + // set up most basic rule-based labeling for layer + QgsRuleBasedLabeling::Rule* root = new QgsRuleBasedLabeling::Rule( 0 ); + + QgsPalLayerSettings s1; + s1.enabled = true; + s1.fieldName = "Class"; + s1.obstacle = false; + s1.dist = 2; + s1.distInMapUnits = false; + root->appendChild( new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings( s1 ) ) ); + + QgsPalLayerSettings s2; + s2.enabled = true; + s2.fieldName = "Class"; + s2.obstacle = false; + s2.dist = 2; + s2.textColor = Qt::red; + root->appendChild( new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings( s2 ), 0, 0, "Class = 'Jet'" ) ); + + vl->labeling().setMode( QgsVectorLayerLabeling::RuleBasedLabels ); + vl->labeling().setRuleBasedLabeling( new QgsRuleBasedLabeling( root ) ); + + QgsMapRendererSequentialJob job( mapSettings ); + job.start(); + job.waitForFinished(); + QImage img = job.renderedImage(); + + img.save( "/tmp/rules.png" ); + + vl->labeling().setMode( QgsVectorLayerLabeling::SimpleLabels ); + + /* + QPainter p( &img ); + QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings ); + context.setPainter( &p ); + + QgsLabelingEngineV2 engine; + engine.setMapSettings( mapSettings ); + engine.addProvider( new QgsRuleBasedLabelProvider( , vl ) ); + engine.run( context );*/ + } QTEST_MAIN( TestQgsLabelingEngineV2 )