Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][labeling] Allow data defined control of label priority
Previously only the priority for the entire layer could be specified
(fix #4606)
  • Loading branch information
nyalldawson committed Jul 8, 2015
1 parent 736f1c4 commit ce2c402
Show file tree
Hide file tree
Showing 10 changed files with 484 additions and 78 deletions.
1 change: 1 addition & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -278,6 +278,7 @@ class QgsPalLayerSettings
Rotation, //data defined rotation
RepeatDistance,
RepeatDistanceUnit,
Priority,

// rendering
ScaleVisibility,
Expand Down
4 changes: 4 additions & 0 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -831,6 +831,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
setDataDefinedProperty( mMaxCharAngleDDBtn, QgsPalLayerSettings::CurvedCharAngleInOut, lyr );
setDataDefinedProperty( mRepeatDistanceDDBtn, QgsPalLayerSettings::RepeatDistance, lyr );
setDataDefinedProperty( mRepeatDistanceUnitDDBtn, QgsPalLayerSettings::RepeatDistanceUnit, lyr );
setDataDefinedProperty( mPriorityDDBtn, QgsPalLayerSettings::Priority, lyr );

// data defined-only
setDataDefinedProperty( mCoordXDDBtn, QgsPalLayerSettings::PositionX, lyr );
Expand Down Expand Up @@ -1051,6 +1052,9 @@ void QgsLabelingGui::populateDataDefinedButtons( QgsPalLayerSettings& s )
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doublePosDesc() );
mLineDistanceUnitDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::DistanceUnits ),
QgsDataDefinedButton::String, QgsDataDefinedButton::unitsMmMuDesc() );
mPriorityDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::Priority ),
QgsDataDefinedButton::AnyType, tr( "double [0.0-10.0]" ) );

// TODO: is this necessary? maybe just use the data defined-only rotation?
//mPointAngleDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::OffsetRotation ),
// QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
Expand Down
6 changes: 6 additions & 0 deletions src/core/pal/feature.cpp
Expand Up @@ -77,6 +77,7 @@ namespace pal
, repeatDist( 0.0 )
, alwaysShow( false )
, mFixedQuadrant( false )
, mPriority( -1.0 )
{
assert( finite( lx ) && finite( ly ) );
}
Expand All @@ -86,6 +87,11 @@ namespace pal

}

double Feature::calculatePriority() const
{
return mPriority >= 0 ? mPriority : layer->getPriority();
}

////////////

FeaturePart::FeaturePart( Feature *feat, const GEOSGeometry* geom )
Expand Down
38 changes: 36 additions & 2 deletions src/core/pal/feature.h
Expand Up @@ -85,24 +85,55 @@ namespace pal

void setLabelInfo( LabelInfo* info ) { labelInfo = info; }
void setDistLabel( double dist ) { distlabel = dist; }
//Set label position of the feature to fixed x/y values
/** Set label position of the feature to fixed x/y values */
void setFixedPosition( double x, double y ) { fixedPos = true; fixedPosX = x; fixedPosY = y;}
void setQuadOffset( double x, double y ) { quadOffset = true; quadOffsetX = x; quadOffsetY = y;}

/** Sets whether the quadrant for the label must be respected. This can be used
* to fix the quadrant for specific features when using an "around point" placement.
* @see fixedQuadrant
*/
void setFixedQuadrant( bool fixed ) { mFixedQuadrant = fixed; }

/** Returns whether the quadrant for the label is fixed.
* @see setFixedQuadrant
*/
bool fixedQuadrant() const { return mFixedQuadrant; }

void setPosOffset( double x, double y ) { offsetPos = true; offsetPosX = x; offsetPosY = y;}
bool fixedPosition() const { return fixedPos; }
//Set label rotation to fixed value

/** Set label rotation to fixed value
*/
void setFixedAngle( double a ) { fixedRotation = true; fixedAngle = a; }
void setRepeatDistance( double dist ) { repeatDist = dist; }
double repeatDistance() const { return repeatDist; }
void setAlwaysShow( bool bl ) { alwaysShow = bl; }

/** Sets the priority for labeling the feature.
* @param priority feature's priority, as a value between 0 (highest priority)
* and 1 (lowest priority). Set to -1.0 to use the layer's default priority
* for this feature.
* @see priority
* @see calculatePriority
*/
void setPriority( double priority ) { mPriority = priority; }

/** Returns the feature's labeling priority.
* @returns feature's priority, as a value between 0 (highest priority)
* and 1 (lowest priority). Returns -1.0 if feature will use the layer's default priority.
* @see setPriority
* @see calculatePriority
*/
double priority() const { return mPriority; }

/** Calculates the priority for the feature. This will be the feature's priority if set,
* otherwise the layer's default priority.
* @see setPriority
* @see priority
*/
double calculatePriority() const;

protected:
Layer *layer;
PalGeometry *userGeom;
Expand Down Expand Up @@ -136,6 +167,9 @@ namespace pal
private:

bool mFixedQuadrant;

//-1 if layer priority should be used
double mPriority;
};

/**
Expand Down
1 change: 0 additions & 1 deletion src/core/pal/layer.h
Expand Up @@ -192,7 +192,6 @@ namespace pal
* \ brief set the layer priority
*
* The best priority is 0, the worst is 1
* Should be links with a slider in a nice gui
*/
void setPriority( double priority );

Expand Down
21 changes: 6 additions & 15 deletions src/core/pal/pal.cpp
Expand Up @@ -206,7 +206,6 @@ namespace pal
QLinkedList<Feats*>* fFeats;
RTree<PointSet*, double, 2, double> *obstacles;
RTree<LabelPosition*, double, 2, double> *candidates;
double priority;
double bbox_min[2];
double bbox_max[2];
} FeatCallBackCtx;
Expand Down Expand Up @@ -275,7 +274,7 @@ namespace pal
ft->shape = NULL;
ft->nblp = nblp;
ft->lPos = lPos;
ft->priority = context->priority;
ft->priority = ft_ptr->getFeature()->calculatePriority();
context->fFeats->append( ft );
}
else
Expand Down Expand Up @@ -320,7 +319,7 @@ namespace pal
return true;
}

Problem* Pal::extract( int nbLayers, const QStringList& layersName, double *layersFactor, double lambda_min, double phi_min, double lambda_max, double phi_max, double scale )
Problem* Pal::extract( int nbLayers, const QStringList& layersName, double lambda_min, double phi_min, double lambda_max, double phi_max, double scale )
{
// to store obstacles
RTree<PointSet*, double, 2, double> *obstacles = new RTree<PointSet*, double, 2, double>();
Expand Down Expand Up @@ -399,7 +398,6 @@ namespace pal


context->layer = layer;
context->priority = layersFactor[i];
// lookup for feature (and generates candidates list)

context->layer->mMutex.lock();
Expand Down Expand Up @@ -614,29 +612,26 @@ namespace pal
int nbLayers = layers->size();

QStringList layersName;
double *priorities = new double[nbLayers];
Layer *layer;
i = 0;
for ( QList<Layer*>::iterator it = layers->begin(); it != layers->end(); ++it )
{
layer = *it;
layersName << layer->name;
priorities[i] = layer->defaultPriority;
i++;
}
mMutex.unlock();

std::list<LabelPosition*> * solution = labeller( nbLayers, layersName, priorities, scale, bbox, stats, displayAll );
std::list<LabelPosition*> * solution = labeller( nbLayers, layersName, scale, bbox, stats, displayAll );

delete[] priorities;
return solution;
}


/*
* BIG MACHINE
*/
std::list<LabelPosition*>* Pal::labeller( int nbLayers, const QStringList& layersName, double *layersFactor, double scale, double bbox[4], PalStat **stats, bool displayAll )
std::list<LabelPosition*>* Pal::labeller( int nbLayers, const QStringList& layersName, double scale, double bbox[4], PalStat **stats, bool displayAll )
{
#ifdef _DEBUG_
std::cout << "LABELLER (selection)" << std::endl;
Expand All @@ -662,7 +657,7 @@ namespace pal

// First, extract the problem
// TODO which is the minimum scale? (> 0, >= 0, >= 1, >1 )
if ( scale < 1 || ( prob = extract( nbLayers, layersName, layersFactor, bbox[0], bbox[1], bbox[2], bbox[3], scale ) ) == NULL )
if ( scale < 1 || ( prob = extract( nbLayers, layersName, bbox[0], bbox[1], bbox[2], bbox[3], scale ) ) == NULL )
{

#ifdef _VERBOSE_
Expand Down Expand Up @@ -750,21 +745,17 @@ namespace pal
int nbLayers = layers->size();

QStringList layersName;
double *priorities = new double[nbLayers];
Layer *layer;
int i = 0;
for ( QList<Layer*>::iterator it = layers->begin(); it != layers->end(); ++it )
{
layer = *it;
layersName << layer->name;
priorities[i] = layer->defaultPriority;
i++;
}
mMutex.unlock();

Problem* prob = extract( nbLayers, layersName, priorities, bbox[0], bbox[1], bbox[2], bbox[3], scale );

delete[] priorities;
Problem* prob = extract( nbLayers, layersName, bbox[0], bbox[1], bbox[2], bbox[3], scale );

return prob;
}
Expand Down
5 changes: 1 addition & 4 deletions src/core/pal/pal.h
Expand Up @@ -202,7 +202,6 @@ namespace pal
*
* @param nbLayers # layers
* @param layersName names of layers to label
* @param layersFactor layers priorities array
* @param scale map scale is '1:scale'
* @param bbox map extent
* @param stat will be filled with labelling process statistics, can be NULL
Expand All @@ -214,7 +213,6 @@ namespace pal
*/
std::list<LabelPosition*> *labeller( int nbLayers,
const QStringList &layersName,
double *layersFactor,
double scale, double bbox[4],
PalStat **stat,
bool displayAll );
Expand Down Expand Up @@ -380,14 +378,13 @@ namespace pal
*
* @param nbLayers number of layers to extract
* @param layersName layers name to be extracted
* @param layersFactor layer's factor (priority between layers, 0 is the best, 1 the worst)
* @param lambda_min xMin bounding-box
* @param phi_min yMin bounding-box
* @param lambda_max xMax bounding-box
* @param phi_max yMax bounding-box
* @param scale the scale (1:scale)
*/
Problem* extract( int nbLayers, const QStringList& layersName, double *layersFactor,
Problem* extract( int nbLayers, const QStringList& layersName,
double lambda_min, double phi_min,
double lambda_max, double phi_max,
double scale );
Expand Down
15 changes: 15 additions & 0 deletions src/core/qgspallabeling.cpp
Expand Up @@ -292,6 +292,8 @@ QgsPalLayerSettings::QgsPalLayerSettings()
mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
mDataDefinedNames.insert( RepeatDistance, QPair<QString, int>( "RepeatDistance", -1 ) );
mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( "RepeatDistanceUnit", -1 ) );
mDataDefinedNames.insert( Priority, QPair<QString, int>( "Priority", -1 ) );

// (data defined only)
mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
Expand Down Expand Up @@ -2173,6 +2175,19 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
feat->setFixedQuadrant( true );
}

// data defined priority?
if ( dataDefinedEvaluate( QgsPalLayerSettings::Priority, exprVal ) )
{
bool ok;
double priorityD = exprVal.toDouble( &ok );
if ( ok )
{
priorityD = qBound( 0.0, priorityD, 10.0 );
priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
feat->setPriority( priorityD );
}
}

//add parameters for data defined labeling to QgsPalGeometry
QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgspallabeling.h
Expand Up @@ -253,6 +253,7 @@ class CORE_EXPORT QgsPalLayerSettings
Rotation = 14, //data defined rotation
RepeatDistance = 84,
RepeatDistanceUnit = 86,
Priority = 87,

// rendering
ScaleVisibility = 23,
Expand Down Expand Up @@ -556,7 +557,7 @@ class CORE_EXPORT QgsPalLayerSettings

void parseDropShadow();

/**Checks if a feature is larger than a minimum size (in mm)
/** Checks if a feature is larger than a minimum size (in mm)
@return true if above size, false if below*/
bool checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const;

Expand Down

0 comments on commit ce2c402

Please sign in to comment.