Skip to content

Commit

Permalink
[labeling] Fix line anchor placements are reversed for labels which
Browse files Browse the repository at this point in the history
sit below lines
  • Loading branch information
nyalldawson committed Feb 3, 2022
1 parent 3ae5e9a commit 75a5bb3
Show file tree
Hide file tree
Showing 39 changed files with 93 additions and 5 deletions.
29 changes: 24 additions & 5 deletions src/core/pal/feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,7 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
expanded = mapShape->clone();
expanded->extendLineByDistance( overrun, overrun, mLF->overrunSmoothDistance() );
mapShape = expanded.get();
shapeLength += 2 * overrun;
}

QgsLabeling::LinePlacementFlags flags = mLF->arrangementFlags();
Expand Down Expand Up @@ -1370,6 +1371,9 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
}
}

// calculate the anchor point for the original line shape as a GEOS point
const geos::unique_ptr originalPoint = mapShape->interpolatePoint( shapeLength * mLF->lineAnchorPercent() );

std::vector< std::unique_ptr< LabelPosition >> positions;
for ( PathOffset offset : { PositiveOffset, NoOffset, NegativeOffset } )
{
Expand All @@ -1394,7 +1398,20 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
if ( qgsDoubleNear( totalDistance, 0.0 ) )
continue;

const double lineAnchorPoint = totalDistance * mLF->lineAnchorPercent();
double lineAnchorPoint = 0;
if ( originalPoint && offset != NoOffset )
{
// the actual anchor point for the offset curves is the closest point on those offset curves
// to the anchor point on the original line. This avoids anchor points which differ greatly
// on the positive/negative offset lines due to line curvature.
lineAnchorPoint = currentMapShape->lineLocatePoint( originalPoint.get() );
}
else
{
lineAnchorPoint = totalDistance * mLF->lineAnchorPercent();
if ( offset == NegativeOffset )
lineAnchorPoint = totalDistance - lineAnchorPoint;
}

if ( pal->isCanceled() )
return 0;
Expand All @@ -1411,16 +1428,21 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
break;

case QgsLabelLineSettings::AnchorType::Strict:
distanceAlongLineToStartCandidate = std::min( lineAnchorPoint, totalDistance * 0.99 - getLabelWidth() );
distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint - getLabelWidth() / 2, 0.0, totalDistance * 0.99 - getLabelWidth() );
singleCandidateOnly = true;
break;
}

bool hasTestedFirstPlacement = false;
for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
{
if ( singleCandidateOnly && hasTestedFirstPlacement )
break;

if ( pal->isCanceled() )
return 0;

hasTestedFirstPlacement = true;
// placements may need to be reversed if using map orientation and the line has right-to-left direction
bool labeledLineSegmentIsRightToLeft = false;
const QgsTextRendererUtils::LabelLineDirection direction = ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? QgsTextRendererUtils::RespectPainterOrientation : QgsTextRendererUtils::FollowLineDirection;
Expand Down Expand Up @@ -1484,9 +1506,6 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq

if ( p )
positions.emplace_back( std::move( p ) );

if ( singleCandidateOnly )
break;
}
}

Expand Down
38 changes: 38 additions & 0 deletions src/core/pal/pointset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,44 @@ void PointSet::getPointByDistance( double *d, double *ad, double dl, double *px,
}
}

geos::unique_ptr PointSet::interpolatePoint( double distance ) const
{
const GEOSGeometry *thisGeos = geos();
if ( !thisGeos )
return nullptr;

try
{
geos::unique_ptr res( GEOSInterpolate_r( QgsGeos::getGEOSHandler(), thisGeos, distance ) );
return res;
}
catch ( GEOSException &e )
{
qWarning( "GEOS exception: %s", e.what() );
return nullptr;
}
}

double PointSet::lineLocatePoint( const GEOSGeometry *point ) const
{
const GEOSGeometry *thisGeos = geos();
if ( !thisGeos )
return -1;

double distance = -1;
try
{
distance = GEOSProject_r( QgsGeos::getGEOSHandler(), thisGeos, point );
}
catch ( GEOSException &e )
{
qWarning( "GEOS exception: %s", e.what() );
return -1;
}

return distance;
}

const GEOSGeometry *PointSet::geos() const
{
if ( !mGeos )
Expand Down
11 changes: 11 additions & 0 deletions src/core/pal/pointset.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

#include "qgis_core.h"
#include "qgsrectangle.h"
#include "qgsgeos.h"

namespace pal
{
Expand Down Expand Up @@ -185,6 +186,16 @@ namespace pal
*/
void getPointByDistance( double *d, double *ad, double dl, double *px, double *py );

/**
* Returns a GEOS geometry representing the point interpolated on the shape by distance.
*/
geos::unique_ptr interpolatePoint( double distance ) const;

/**
* Returns the distance along the geometry closest to the specified GEOS \a point.
*/
double lineLocatePoint( const GEOSGeometry *point ) const;

/**
* Returns the point set's GEOS geometry.
*/
Expand Down
20 changes: 20 additions & 0 deletions tests/src/core/testqgslabelingengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3631,6 +3631,26 @@ void TestQgsLabelingEngine::testLineAnchorCurved()

img = job2.renderedImage();
QVERIFY( imageCheck( QStringLiteral( "curved_anchor_end" ), img, 20 ) );

settings.lineSettings().setLineAnchorPercent( 0.3 );
settings.lineSettings().setAnchorType( QgsLabelLineSettings::AnchorType::Strict );
vl2->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) );
QgsMapRendererSequentialJob job3( mapSettings );
job3.start();
job3.waitForFinished();

img = job3.renderedImage();
QVERIFY( imageCheck( QStringLiteral( "curved_anchor_30_above" ), img, 20 ) );

settings.lineSettings().setPlacementFlags( QgsLabeling::LinePlacementFlag::BelowLine );
vl2->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) );
QgsMapRendererSequentialJob job4( mapSettings );
job4.start();
job4.waitForFinished();

img = job4.renderedImage();
QVERIFY( imageCheck( QStringLiteral( "curved_anchor_30_below" ), img, 20 ) );

}

void TestQgsLabelingEngine::testLineAnchorCurvedConstraints()
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.

0 comments on commit 75a5bb3

Please sign in to comment.