Skip to content
Permalink
Browse files

[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.
  • Loading branch information
wonder-sk committed Sep 24, 2015
1 parent 287590e commit 1782d1a50592624089067b49951f36161b825fea
@@ -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++ )
{

0 comments on commit 1782d1a

Please sign in to comment.
You can’t perform that action at this time.