'Using perimeter (curved)' labels on polygon layers will now respect 'Show upside-down labels' setting.

This commit is contained in:
Frits van Veen 2016-08-04 14:53:19 +02:00 committed by Nyall Dawson
parent d6b419ab18
commit 34e2bea246
6 changed files with 177 additions and 146 deletions

View File

@ -968,7 +968,7 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList<LabelPosition*>& l
} }
LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, int orientation, int index, double distance ) LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, int& orientation, int index, double distance, bool& flip )
{ {
// Check that the given distance is on the given index and find the correct index and distance if not // Check that the given distance is on the given index and find the correct index and distance if not
while ( distance < 0 && index > 1 ) while ( distance < 0 && index > 1 )
@ -995,10 +995,6 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
LabelInfo* li = mLF->curvedLabelInfo(); LabelInfo* li = mLF->curvedLabelInfo();
// Keep track of the initial index,distance incase we need to re-call get_placement_offset
int initial_index = index;
double initial_distance = distance;
double string_height = li->label_height; double string_height = li->label_height;
double old_x = path_positions->x[index-1]; double old_x = path_positions->x[index-1];
double old_y = path_positions->y[index-1]; double old_y = path_positions->y[index-1];
@ -1018,14 +1014,18 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
LabelPosition* slp = nullptr; LabelPosition* slp = nullptr;
LabelPosition* slp_tmp = nullptr; LabelPosition* slp_tmp = nullptr;
// current_placement = placement_result()
double angle = atan2( -dy, dx ); double angle = atan2( -dy, dx );
bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller
if ( !orientation_forced ) if ( !orientation_forced )
orientation = ( angle > 0.55 * M_PI || angle < -0.45 * M_PI ? -1 : 1 ); orientation = ( angle > 0.55 * M_PI || angle < -0.45 * M_PI ? -1 : 1 );
int upside_down_char_count = 0; // Count of characters that are placed upside down. if ( !isUprightLabel() )
{
if ( orientation != 1 )
flip = true; // Report to the caller, that the orientation is flipped
orientation = 1;
}
for ( int i = 0; i < li->char_num; i++ ) for ( int i = 0; i < li->char_num; i++ )
{ {
@ -1086,7 +1086,6 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
// Calculate angle from the start of the character to the end based on start_/end_ position // Calculate angle from the start of the character to the end based on start_/end_ position
angle = atan2( start_y - end_y, end_x - start_x ); angle = atan2( start_y - end_y, end_x - start_x );
//angle = atan2(end_y-start_y, end_x-start_x);
// Test last_character_angle vs angle // Test last_character_angle vs angle
// since our rendering angle has changed then check against our // since our rendering angle has changed then check against our
@ -1108,7 +1107,10 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
// and we're calculating the mean line here // and we're calculating the mean line here
double dist = 0.9 * li->label_height / 2; double dist = 0.9 * li->label_height / 2;
if ( orientation < 0 ) if ( orientation < 0 )
{
dist = -dist; dist = -dist;
flip = true;
}
start_x += dist * cos( angle + M_PI_2 ); start_x += dist * cos( angle + M_PI_2 );
start_y -= dist * sin( angle + M_PI_2 ); start_y -= dist * sin( angle + M_PI_2 );
@ -1137,36 +1139,15 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
slp_tmp->setNextPart( tmp ); slp_tmp->setNextPart( tmp );
slp_tmp = tmp; slp_tmp = tmp;
//current_placement.add_node(ci.character,render_x, -render_y, render_angle);
//current_placement.add_node(ci.character,render_x - current_placement.starting_x, render_y - current_placement.starting_y, render_angle)
// Normalise to 0 <= angle < 2PI // Normalise to 0 <= angle < 2PI
while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI; while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
while ( render_angle < 0 ) render_angle += 2 * M_PI; while ( render_angle < 0 ) render_angle += 2 * M_PI;
if ( render_angle > M_PI / 2 && render_angle < 1.5 * M_PI ) if ( render_angle > M_PI / 2 && render_angle < 1.5 * M_PI )
upside_down_char_count++; slp->incrementUpsideDownCharCount();
} }
// END FOR // END FOR
// If we placed too many characters upside down
if ( upside_down_char_count >= li->char_num / 2.0 )
{
// if we auto-detected the orientation then retry with the opposite orientation
if ( !orientation_forced )
{
orientation = -orientation;
delete slp;
slp = curvedPlacementAtOffset( path_positions, path_distances, orientation, initial_index, initial_distance );
}
else
{
// Otherwise we have failed to find a placement
delete slp;
return nullptr;
}
}
return slp; return slp;
} }
@ -1231,26 +1212,47 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
flags = FLAG_ON_LINE; // default flag flags = FLAG_ON_LINE; // default flag
// placements may need to be reversed if using line position dependent orientation // placements may need to be reversed if using line position dependent orientation
// and the line has right-to-left direction // and the line has right-to-left direction
bool reversed = ( !( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false ); bool reversed = (( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false );
// an orientation of 0 means try both orientations and choose the best // an orientation of 0 means try both orientations and choose the best
int orientation = 0; int orientation = 0;
if ( !( flags & FLAG_MAP_ORIENTATION ) if ( !( flags & FLAG_MAP_ORIENTATION ) )
&& mLF->layer()->arrangement() == QgsPalLayerSettings::PerimeterCurved )
{ {
//... but if we are labeling the perimeter of a polygon and using line orientation flags, //... but if we are using line orientation flags, then we can only accept a single orientation,
// then we can only accept a single orientation, as we need to ensure that the labels fall // as we need to ensure that the labels fall inside or outside the polyline or polygon (and not mixed)
// inside or outside the polygon (and not mixed)
orientation = reversed ? -1 : 1; orientation = reversed ? -1 : 1;
} }
// generate curved labels // generate curved labels
for ( int i = 0; i*delta < total_distance; i++ ) for ( int i = 0; i*delta < total_distance; i++ )
{ {
LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta ); bool flip = false;
bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller
LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta, flip );
if ( slp == nullptr )
continue;
if ( slp ) // If we placed too many characters upside down
if ( slp->upsideDownCharCount() >= li->char_num / 2.0 )
{ {
// if we auto-detected the orientation then retry with the opposite orientation
if ( !orientation_forced )
{
orientation = -orientation;
delete slp;
slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta, flip );
}
else if ( isUprightLabel() && !flip )
{
// Retry with the opposite orientation
orientation = -orientation;
delete slp;
slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta, flip );
}
}
if ( slp == nullptr )
continue;
// evaluate cost // evaluate cost
double angle_diff = 0.0, angle_last = 0.0, diff; double angle_diff = 0.0, angle_last = 0.0, diff;
LabelPosition* tmp = slp; LabelPosition* tmp = slp;
@ -1283,18 +1285,19 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
// average angle is calculated with respect to periodicity of angles // average angle is calculated with respect to periodicity of angles
double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num ); double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num );
bool localreversed = flip ? !reversed : reversed;
// displacement - we loop through 3 times, generating above, online then below line placements successively // displacement - we loop through 3 times, generating above, online then below line placements successively
for ( int i = 0; i <= 2; ++i ) for ( int i = 0; i <= 2; ++i )
{ {
LabelPosition* p = nullptr; LabelPosition* p = nullptr;
if ( i == 0 && (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) ) ) if ( i == 0 && (( !localreversed && ( flags & FLAG_ABOVE_LINE ) ) || ( localreversed && ( flags & FLAG_BELOW_LINE ) ) ) )
p = _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 ); p = _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 );
if ( i == 1 && flags & FLAG_ON_LINE ) if ( i == 1 && flags & FLAG_ON_LINE )
{ {
p = _createCurvedCandidate( slp, angle_avg, 0 ); p = _createCurvedCandidate( slp, angle_avg, 0 );
p->setCost( p->cost() + 0.002 ); p->setCost( p->cost() + 0.002 );
} }
if ( i == 2 && (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) ) ) if ( i == 2 && (( !localreversed && ( flags & FLAG_BELOW_LINE ) ) || ( localreversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
{ {
p = _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() ); p = _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() );
p->setCost( p->cost() + 0.001 ); p->setCost( p->cost() + 0.001 );
@ -1319,11 +1322,10 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
if ( p ) if ( p )
positions.append( p ); positions.append( p );
} }
// delete original candidate // delete original candidate
delete slp; delete slp;
} }
}
int nbp = positions.size(); int nbp = positions.size();
for ( int i = 0; i < nbp; i++ ) for ( int i = 0; i < nbp; i++ )
@ -1336,9 +1338,6 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
return nbp; return nbp;
} }
/* /*
* seg 2 * seg 2
* pt3 ____________pt2 * pt3 ____________pt2
@ -1588,9 +1587,7 @@ int FeaturePart::createCandidates( QList< LabelPosition*>& lPos,
createCandidatesAroundPoint( x[0], y[0], lPos, angle ); createCandidatesAroundPoint( x[0], y[0], lPos, angle );
break; break;
case GEOS_LINESTRING: case GEOS_LINESTRING:
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Curved ) if ( mLF->layer()->isCurved() )
createCurvedCandidatesAlongLine( lPos, mapShape );
else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::PerimeterCurved )
createCurvedCandidatesAlongLine( lPos, mapShape ); createCurvedCandidatesAlongLine( lPos, mapShape );
else else
createCandidatesAlongLine( lPos, mapShape ); createCandidatesAlongLine( lPos, mapShape );
@ -1774,3 +1771,28 @@ double FeaturePart::calculatePriority() const
return mLF->priority() >= 0 ? mLF->priority() : mLF->layer()->priority(); return mLF->priority() >= 0 ? mLF->priority() : mLF->layer()->priority();
} }
bool FeaturePart::isUprightLabel() const
{
bool uprightLabel = false;
switch ( mLF->layer()->upsidedownLabels() )
{
case Layer::Upright:
uprightLabel = true;
break;
case Layer::ShowDefined:
// upright only dynamic labels
if ( !hasFixedRotation() || ( !hasFixedPosition() && fixedAngle() == 0.0 ) )
{
uprightLabel = true;
}
break;
case Layer::ShowAll:
break;
default:
uprightLabel = true;
}
return uprightLabel;
}

View File

@ -178,7 +178,7 @@ namespace pal
int createCandidatesAlongLineNearMidpoint( QList<LabelPosition *> &lPos, PointSet *mapShape, double initialCost = 0.0 ); int createCandidatesAlongLineNearMidpoint( QList<LabelPosition *> &lPos, PointSet *mapShape, double initialCost = 0.0 );
LabelPosition* curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, LabelPosition* curvedPlacementAtOffset( PointSet* path_positions, double* path_distances,
int orientation, int index, double distance ); int& orientation, int index, double distance, bool& flip );
/** Generate curved candidates for line features. /** Generate curved candidates for line features.
* @param lPos pointer to an array of candidates, will be filled by generated candidates * @param lPos pointer to an array of candidates, will be filled by generated candidates
@ -213,13 +213,28 @@ namespace pal
double getLabelHeight() const { return mLF->size().height(); } double getLabelHeight() const { return mLF->size().height(); }
double getLabelDistance() const { return mLF->distLabel(); } double getLabelDistance() const { return mLF->distLabel(); }
bool getFixedRotation() { return mLF->hasFixedAngle(); } //! Returns true if the feature's label has a fixed rotation
double getLabelAngle() { return mLF->fixedAngle(); } bool hasFixedRotation() const { return mLF->hasFixedAngle(); }
bool getFixedPosition() { return mLF->hasFixedPosition(); }
bool getAlwaysShow() { return mLF->alwaysShow(); } //! Returns the fixed angle for the feature's label
bool isObstacle() { return mLF->isObstacle(); } double fixedAngle() const { return mLF->fixedAngle(); }
double obstacleFactor() { return mLF->obstacleFactor(); }
double repeatDistance() { return mLF->repeatDistance(); } //! Returns true if the feature's label has a fixed position
bool hasFixedPosition() const { return mLF->hasFixedPosition(); }
//! Returns true if the feature's label should always been shown,
//! even when it collides with other labels
bool alwaysShow() const { return mLF->alwaysShow(); }
//! Returns true if the feature should act as an obstacle to labels
bool isObstacle() const { return mLF->isObstacle(); }
//! Returns the feature's obstacle factor, which represents the penalty
//! incurred for a label to overlap the feature
double obstacleFactor() const { return mLF->obstacleFactor(); }
//! Returns the distance between repeating labels for this feature
double repeatDistance() const { return mLF->repeatDistance(); }
//! Get number of holes (inner rings) - they are considered as obstacles //! Get number of holes (inner rings) - they are considered as obstacles
int getNumSelfObstacles() const { return mHoles.count(); } int getNumSelfObstacles() const { return mHoles.count(); }
@ -242,6 +257,8 @@ namespace pal
*/ */
double calculatePriority() const; double calculatePriority() const;
//! Returns true if feature's label must be displayed upright
bool isUprightLabel() const;
protected: protected:

View File

@ -60,6 +60,7 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h,
, quadrant( quadrant ) , quadrant( quadrant )
, mCost( cost ) , mCost( cost )
, mHasObstacleConflict( false ) , mHasObstacleConflict( false )
, mUpsideDownCharCount( 0 )
{ {
type = GEOS_POLYGON; type = GEOS_POLYGON;
nbPoints = 4; nbPoints = 4;
@ -77,8 +78,6 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h,
double dx1, dx2, dy1, dy2; double dx1, dx2, dy1, dy2;
double tx, ty;
dx1 = cos( this->alpha ) * w; dx1 = cos( this->alpha ) * w;
dy1 = sin( this->alpha ) * w; dy1 = sin( this->alpha ) * w;
@ -98,32 +97,14 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h,
y[3] = y1 + dy2; y[3] = y1 + dy2;
// upside down ? (curved labels are always correct) // upside down ? (curved labels are always correct)
if ( feature->layer()->arrangement() != QgsPalLayerSettings::Curved && if ( !feature->layer()->isCurved() &&
feature->layer()->arrangement() != QgsPalLayerSettings::PerimeterCurved &&
this->alpha > M_PI / 2 && this->alpha <= 3*M_PI / 2 ) this->alpha > M_PI / 2 && this->alpha <= 3*M_PI / 2 )
{ {
bool uprightLabel = false; if ( feature->isUprightLabel() )
{
// Turn label upsidedown by inverting boundary points
double tx, ty;
switch ( feature->layer()->upsidedownLabels() )
{
case Layer::Upright:
uprightLabel = true;
break;
case Layer::ShowDefined:
// upright only dynamic labels
if ( !feature->getFixedRotation() || ( !feature->getFixedPosition() && feature->getLabelAngle() == 0.0 ) )
{
uprightLabel = true;
}
break;
case Layer::ShowAll:
break;
default:
uprightLabel = true;
}
if ( uprightLabel )
{
tx = x[0]; tx = x[0];
ty = y[0]; ty = y[0];
@ -184,6 +165,7 @@ LabelPosition::LabelPosition( const LabelPosition& other )
reversed = other.reversed; reversed = other.reversed;
quadrant = other.quadrant; quadrant = other.quadrant;
mHasObstacleConflict = other.mHasObstacleConflict; mHasObstacleConflict = other.mHasObstacleConflict;
mUpsideDownCharCount = other.mUpsideDownCharCount;
} }
bool LabelPosition::isIn( double *bbox ) bool LabelPosition::isIn( double *bbox )

View File

@ -228,6 +228,11 @@ namespace pal
int getPartId() const { return partId; } int getPartId() const { return partId; }
void setPartId( int id ) { partId = id; } void setPartId( int id ) { partId = id; }
//! Increases the count of upside down characters for this label position
int incrementUpsideDownCharCount() { return ++mUpsideDownCharCount; }
//! Returns the number of upside down characters for this label position
int upsideDownCharCount() const { return mUpsideDownCharCount; }
void removeFromIndex( RTree<LabelPosition*, double, 2, double> *index ); void removeFromIndex( RTree<LabelPosition*, double, 2, double> *index );
void insertIntoIndex( RTree<LabelPosition*, double, 2, double> *index ); void insertIntoIndex( RTree<LabelPosition*, double, 2, double> *index );
@ -296,6 +301,7 @@ namespace pal
private: private:
double mCost; double mCost;
bool mHasObstacleConflict; bool mHasObstacleConflict;
int mUpsideDownCharCount;
/** Calculates the total number of parts for this label position /** Calculates the total number of parts for this label position
*/ */

View File

@ -94,6 +94,10 @@ namespace pal
*/ */
QgsPalLayerSettings::Placement arrangement() const { return mArrangement; } QgsPalLayerSettings::Placement arrangement() const { return mArrangement; }
/** Returns true if the layer has curved labels
*/
bool isCurved() const { return mArrangement == QgsPalLayerSettings::Curved || mArrangement == QgsPalLayerSettings::PerimeterCurved; }
/** Sets the layer's arrangement policy. /** Sets the layer's arrangement policy.
* @param arrangement arrangement policy * @param arrangement arrangement policy
* @see arrangement * @see arrangement

View File

@ -2268,7 +2268,7 @@ QList<LabelPosition*> * Problem::getSolution( bool returnInactive )
} }
else if ( returnInactive else if ( returnInactive
|| mLabelPositions.at( featStartId[i] )->getFeaturePart()->layer()->displayAll() || mLabelPositions.at( featStartId[i] )->getFeaturePart()->layer()->displayAll()
|| mLabelPositions.at( featStartId[i] )->getFeaturePart()->getAlwaysShow() ) || mLabelPositions.at( featStartId[i] )->getFeaturePart()->alwaysShow() )
{ {
solList->push_back( mLabelPositions.at( featStartId[i] ) ); // unplaced label solList->push_back( mLabelPositions.at( featStartId[i] ) ); // unplaced label
} }