mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[FEATURE] Rule-based labeling
Work in progress This code has been funded by Tuscany Region (Italy) - SITA (CIG: 63526840AE) and commissioned to Gis3W s.a.s.
This commit is contained in:
parent
287590eea2
commit
1782d1a505
@ -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
|
||||
|
@ -388,11 +388,6 @@ namespace pal
|
||||
}
|
||||
}
|
||||
|
||||
QString LabelPosition::getLayerName() const
|
||||
{
|
||||
return feature->layer()->name();
|
||||
}
|
||||
|
||||
void LabelPosition::setConflictsWithObstacle( bool conflicts )
|
||||
{
|
||||
mHasObstacleConflict = conflicts;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 )
|
||||
|
@ -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<FeaturePart*> 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() );
|
||||
|
@ -90,38 +90,17 @@ namespace pal
|
||||
|
||||
}
|
||||
|
||||
QList<Layer*> 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<FeaturePart*, double, 2, double> *obstacles = new RTree<FeaturePart*, double, 2, double>();
|
||||
@ -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<LabelPosition*>* Pal::labeller( double bbox[4], PalStat **stats, bool displayAll )
|
||||
{
|
||||
return labeller( mLayers.keys(), bbox, stats, displayAll );
|
||||
}
|
||||
|
||||
/*
|
||||
* BIG MACHINE
|
||||
*/
|
||||
std::list<LabelPosition*>* Pal::labeller( const QStringList& layerNames, double bbox[4], PalStat **stats, bool displayAll )
|
||||
std::list<LabelPosition*>* 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<LabelPosition*>* Pal::solveProblem( Problem* prob, bool displayAll )
|
||||
|
@ -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<Layer*> 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<LabelPosition*> *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<LabelPosition*> *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 );
|
||||
|
||||
|
||||
|
@ -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++ )
|
||||
{
|
||||
|
@ -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<QgsLabelFeature*> 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<QgsLabelFeature*> 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 )
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <QFlags>
|
||||
|
||||
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<QgsLabelFeature*> 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<QgsAbstractLabelProvider*> subProviders() { return QList<QgsAbstractLabelProvider*>(); }
|
||||
|
||||
//! 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<QgsAbstractLabelProvider*> mProviders;
|
||||
QList<QgsAbstractLabelProvider*> mSubProviders;
|
||||
//! Flags
|
||||
Flags mFlags;
|
||||
//! search method to use for removal collisions between labels
|
||||
|
@ -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
|
||||
|
183
src/core/qgsrulebasedlabeling.cpp
Normal file
183
src/core/qgsrulebasedlabeling.cpp
Normal file
@ -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<QgsAbstractLabelProvider*> QgsRuleBasedLabelProvider::subProviders()
|
||||
{
|
||||
QList<QgsAbstractLabelProvider*> 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<QgsRenderContext&>( 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;
|
||||
}
|
189
src/core/qgsrulebasedlabeling.h
Normal file
189
src/core/qgsrulebasedlabeling.h
Normal file
@ -0,0 +1,189 @@
|
||||
#ifndef QGSRULEBASEDLABELING_H
|
||||
#define QGSRULEBASEDLABELING_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
class QgsExpression;
|
||||
class QgsFeature;
|
||||
class QgsPalLayerSettings;
|
||||
class QgsRenderContext;
|
||||
class QgsVectorLayer;
|
||||
class QgsVectorLayerLabelProvider;
|
||||
|
||||
|
||||
class CORE_EXPORT QgsRuleBasedLabeling
|
||||
{
|
||||
public:
|
||||
class Rule;
|
||||
typedef QList<Rule*> RuleList;
|
||||
typedef QMap<Rule*, QgsVectorLayerLabelProvider*> 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<QgsAbstractLabelProvider*> subProviders() override;
|
||||
|
||||
protected:
|
||||
//! owned copy
|
||||
QgsRuleBasedLabeling mRules;
|
||||
//! label providers are owned by labeling engine
|
||||
QgsRuleBasedLabeling::RuleToProviderMap mSubProviders;
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSRULEBASEDLABELING_H
|
@ -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;
|
||||
|
||||
|
@ -63,6 +63,7 @@ class QgsSymbolV2;
|
||||
class QgsVectorDataProvider;
|
||||
class QgsVectorLayerEditBuffer;
|
||||
class QgsVectorLayerJoinBuffer;
|
||||
class QgsVectorLayerLabeling;
|
||||
class QgsPointV2;
|
||||
|
||||
typedef QList<int> 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;
|
||||
|
||||
|
@ -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<QgsLabelFeature*> 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 );
|
||||
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,6 @@ class CORE_EXPORT QgsVectorLayerDiagramProvider : public QgsAbstractLabelProvide
|
||||
//! Clean up
|
||||
~QgsVectorLayerDiagramProvider();
|
||||
|
||||
virtual QString id() const override;
|
||||
|
||||
virtual QList<QgsLabelFeature*> labelFeatures( const QgsRenderContext& context ) override;
|
||||
|
||||
virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override;
|
||||
|
75
src/core/qgsvectorlayerlabeling.cpp
Normal file
75
src/core/qgsvectorlayerlabeling.cpp
Normal file
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
49
src/core/qgsvectorlayerlabeling.h
Normal file
49
src/core/qgsvectorlayerlabeling.h
Normal file
@ -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
|
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<QgsLabelFeature*> labelFeatures( const QgsRenderContext& context ) override;
|
||||
|
||||
virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 )
|
||||
|
Loading…
x
Reference in New Issue
Block a user