Skip to content

Commit

Permalink
[labeling] Fix candidate generation for horizontal line labels
Browse files Browse the repository at this point in the history
The previous candidate generation code just used the exact same
logic as parallel line labels but forced the angle to be horizontal.

This was completely wrong, because the parallel line candidate generation
is totally designed around parallel labels.

Instead, use specific logic for horizontal line candidate generation,
resulting in optimal candidate generation for horizontal labels.
  • Loading branch information
nyalldawson committed Feb 19, 2020
1 parent 4b22623 commit 69c4013
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
63 changes: 62 additions & 1 deletion src/core/pal/feature.cpp
Expand Up @@ -696,6 +696,64 @@ std::size_t FeaturePart::createCandidatesAlongLine( std::vector< std::unique_ptr
return candidates;
}

std::size_t FeaturePart::createHorizontalCandidatesAlongLine( std::vector<std::unique_ptr<LabelPosition> > &lPos, PointSet *mapShape, Pal *pal )
{
const double labelWidth = getLabelWidth();
const double labelHeight = getLabelHeight();

PointSet *line = mapShape;
int nbPoints = line->nbPoints;
std::vector< double > &x = line->x;
std::vector< double > &y = line->y;

std::vector< double > segmentLengths( nbPoints - 1 ); // segments lengths distance bw pt[i] && pt[i+1]
std::vector< double >distanceToSegment( nbPoints ); // absolute distance bw pt[0] and pt[i] along the line

double totalLineLength = 0.0; // line length
for ( int i = 0; i < line->nbPoints - 1; i++ )
{
if ( i == 0 )
distanceToSegment[i] = 0;
else
distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];

segmentLengths[i] = GeomFunction::dist_euc2d( x[i], y[i], x[i + 1], y[i + 1] );
totalLineLength += segmentLengths[i];
}
distanceToSegment[line->nbPoints - 1] = totalLineLength;

const std::size_t candidateTargetCount = maximumLineCandidates();
const double lineStepDistance = totalLineLength / ( candidateTargetCount + 1 ); // distance to move along line with each candidate
double currentDistanceAlongLine = lineStepDistance;

double candidateCenterX, candidateCenterY;
int i = 0;
while ( currentDistanceAlongLine < totalLineLength )
{
if ( pal->isCanceled() )
{
return lPos.size();
}

line->getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );

// penalize positions which are further from the line's midpoint
double cost = std::fabs( totalLineLength / 2 - currentDistanceAlongLine ) / totalLineLength; // <0, 0.5>
cost /= 1000; // < 0, 0.0005 >

lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateCenterX - labelWidth / 2, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost, this ) );

currentDistanceAlongLine += lineStepDistance;

i++;

if ( lineStepDistance < 0 )
break;
}

return lPos.size();
}

std::size_t FeaturePart::createCandidatesAlongLineNearStraightSegments( std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal )
{
double labelWidth = getLabelWidth();
Expand Down Expand Up @@ -1718,8 +1776,11 @@ std::vector< std::unique_ptr< LabelPosition > > FeaturePart::createCandidates( P
else
createCandidatesAroundPoint( x[0], y[0], lPos, angle );
break;

case GEOS_LINESTRING:
if ( mLF->layer()->isCurved() )
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal )
createHorizontalCandidatesAlongLine( lPos, this, pal );
else if ( mLF->layer()->isCurved() )
createCurvedCandidatesAlongLine( lPos, this, true, pal );
else
createCandidatesAlongLine( lPos, this, true, pal );
Expand Down
9 changes: 9 additions & 0 deletions src/core/pal/feature.h
Expand Up @@ -194,6 +194,15 @@ namespace pal
*/
std::size_t createCandidatesAlongLine( std::vector<std::unique_ptr<LabelPosition> > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal );

/**
* Generate horizontal candidates for line feature.
* \param lPos pointer to an array of candidates, will be filled by generated candidates
* \param mapShape a pointer to the line
* \param pal point to pal settings object, for cancellation support
* \returns the number of generated candidates
*/
std::size_t createHorizontalCandidatesAlongLine( std::vector<std::unique_ptr<LabelPosition> > &lPos, PointSet *mapShape, Pal *pal );

/**
* Generate candidates for line feature, by trying to place candidates towards the middle of the longest
* straightish segments of the line. Segments closer to horizontal are preferred over vertical segments.
Expand Down

0 comments on commit 69c4013

Please sign in to comment.