Skip to content

Commit

Permalink
Address review
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn authored and nyalldawson committed Apr 29, 2021
1 parent 76ee528 commit 8f7ecd9
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 43 deletions.
55 changes: 13 additions & 42 deletions src/core/pal/feature.cpp
Expand Up @@ -1318,7 +1318,7 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
const double offsetDistance = mLF->distLabel() + li->characterHeight() / 2;
std::unique_ptr< PointSet > mapShapeOffsetPositive;
std::unique_ptr< PointSet > mapShapeOffsetNegative;
if ( hasAboveBelowLinePlacement )
if ( hasAboveBelowLinePlacement && !qgsDoubleNear( offsetDistance, 0 ) )
{
// create offseted map shapes to be used for above and below line placements
mapShapeOffsetPositive = mapShape->clone();
Expand All @@ -1330,6 +1330,7 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
}
else
{
// In case of a negative offset distance, above line placement switch to below line and vice versa
if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
&& !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
{
Expand All @@ -1348,39 +1349,26 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
}

std::vector< std::unique_ptr< LabelPosition >> positions;
for ( int i = 0; i <= 2; i++ )
for ( PathOffset offset : { PositiveOffset, NoOffset, NegativeOffset } )
{
PointSet *currentMapShape = nullptr;
if ( i == 0 && hasAboveBelowLinePlacement )
if ( offset == PositiveOffset && hasAboveBelowLinePlacement )
{
currentMapShape = mapShapeOffsetPositive.get();
}
if ( i == 1 && flags & QgsLabeling::LinePlacementFlag::OnLine )
if ( offset == NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
{
currentMapShape = mapShape;
}
if ( i == 2 && hasAboveBelowLinePlacement )
if ( offset == NegativeOffset && hasAboveBelowLinePlacement )
{
currentMapShape = mapShapeOffsetNegative.get();
}
if ( !currentMapShape )
continue;

// distance calculation
std::vector< double > pathDistances( currentMapShape->nbPoints );
double totalDistance = 0;
double oldX = -1.0, oldY = -1.0;
for ( int i = 0; i < currentMapShape->nbPoints; i++ )
{
if ( i == 0 )
pathDistances[i] = 0;
else
pathDistances[i] = std::sqrt( std::pow( oldX - currentMapShape->x[i], 2 ) + std::pow( oldY - currentMapShape->y[i], 2 ) );
oldX = currentMapShape->x[i];
oldY = currentMapShape->y[i];

totalDistance += pathDistances[i];
}
const auto [ pathDistances, totalDistance ] = currentMapShape->edgeDistances();
if ( qgsDoubleNear( totalDistance, 0.0 ) )
continue;

Expand Down Expand Up @@ -1418,35 +1406,18 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq

if ( !labelPosition )
continue;
if ( ( i == 0 || i == 2 ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
if ( ( offset != NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
continue;
if ( ( i == 0 || i == 2 ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
if ( ( offset != NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
continue;

// evaluate cost
double angleDiff = 0.0, angleLast = 0.0, diff;
LabelPosition *tmp = labelPosition.get();
double sinAvg = 0, cosAvg = 0;
while ( tmp )
{
if ( tmp != labelPosition.get() ) // not first?
{
diff = std::fabs( tmp->getAlpha() - angleLast );
if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
diff = std::min( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
angleDiff += diff;
}

sinAvg += std::sin( tmp->getAlpha() );
cosAvg += std::cos( tmp->getAlpha() );
angleLast = tmp->getAlpha();
tmp = tmp->nextPart();
}
const double angleDiff = labelPosition->angleDifferential();
const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0; // <0, pi> but pi/8 is much already

// if anchor placement is towards start or end of line, we need to slightly tweak the costs to ensure that the
// anchor weighting is sufficient to push labels towards start/end
const bool anchorIsFlexiblePlacement = !singleCandidateOnly && mLF->lineAnchorPercent() > 0.1 && mLF->lineAnchorPercent() < 0.9;
double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0; // <0, pi> but pi/8 is much already
double cost = angleDiffAvg / 100; // <0, 0.031 > but usually <0, 0.003 >
if ( cost < 0.0001 )
cost = 0.0001;
Expand All @@ -1456,13 +1427,13 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
double costCenter = std::fabs( lineAnchorPoint - labelCenter ) / totalDistance; // <0, 0.5>
cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 ); // < 0, 0.005 >, or <0, 0.05> if preferring placement close to start/end of line

const bool isBelow = ( i == 0 || i == 2 ) && labeledLineSegmentIsRightToLeft;
const bool isBelow = ( offset != NoOffset ) && labeledLineSegmentIsRightToLeft;
if ( isBelow )
{
// add additional cost for on line placement
cost += 0.001;
}
else if ( i == 1 )
else if ( offset == NoOffset )
{
// add additional cost for below line placement
cost += 0.002;
Expand Down
10 changes: 9 additions & 1 deletion src/core/pal/feature.h
Expand Up @@ -66,6 +66,14 @@ namespace pal

public:

//! Path offset variances used in curved placement.
enum PathOffset
{
NoOffset,
PositiveOffset,
NegativeOffset
};

/**
* Creates a new generic feature.
* \param lf a pointer for a feature which contains the spatial entites
Expand All @@ -76,7 +84,7 @@ namespace pal
FeaturePart( const FeaturePart &other );

/**
* Delete the feature
* Deletes the feature.
*/
~FeaturePart() override;

Expand Down
23 changes: 23 additions & 0 deletions src/core/pal/labelposition.cpp
Expand Up @@ -646,3 +646,26 @@ double LabelPosition::polygonIntersectionCostForParts( PointSet *polygon ) const

return cost;
}

double LabelPosition::angleDifferential()
{
double angleDiff = 0.0, angleLast = 0.0, diff;
double sinAvg = 0, cosAvg = 0;
LabelPosition *tmp = this;
while ( tmp )
{
if ( tmp != this ) // not first?
{
diff = std::fabs( tmp->getAlpha() - angleLast );
if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
diff = std::min( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
angleDiff += diff;
}

sinAvg += std::sin( tmp->getAlpha() );
cosAvg += std::cos( tmp->getAlpha() );
angleLast = tmp->getAlpha();
tmp = tmp->nextPart();
}
return angleDiff;
}
5 changes: 5 additions & 0 deletions src/core/pal/labelposition.h
Expand Up @@ -336,6 +336,11 @@ namespace pal
*/
void setGlobalId( unsigned int id ) { mGlobalId = id; }

/**
* Returns the angle differential of all LabelPosition parts
*/
double angleDifferential();

protected:

int id;
Expand Down
19 changes: 19 additions & 0 deletions src/core/pal/pointset.cpp
Expand Up @@ -1094,3 +1094,22 @@ QString PointSet::toWkt() const
return QString();
}
}

std::tuple< std::vector< double >, double > PointSet::edgeDistances() const
{
std::vector< double > distances( nbPoints );
double totalDistance = 0;
double oldX = -1.0, oldY = -1.0;
for ( int i = 0; i < nbPoints; i++ )
{
if ( i == 0 )
distances[i] = 0;
else
distances[i] = std::sqrt( std::pow( oldX - x[i], 2 ) + std::pow( oldY - y[i], 2 ) );

oldX = x[i];
oldY = y[i];
totalDistance += distances[i];
}
return std::make_tuple( std::move( distances ), totalDistance );
}
5 changes: 5 additions & 0 deletions src/core/pal/pointset.h
Expand Up @@ -210,6 +210,11 @@ namespace pal
*/
QString toWkt() const;

/**
* Returns a vector of edge distances as well as its total length
*/
std::tuple< std::vector< double >, double > edgeDistances() const;

int nbPoints;
std::vector< double > x;
std::vector< double > y; // points order is counterclockwise
Expand Down

0 comments on commit 8f7ecd9

Please sign in to comment.