Skip to content

Commit dc0cc32

Browse files
committed
Further tweaks to line labeling, add tests
Sponsored by Andreas Neumann
1 parent 3b940fb commit dc0cc32

File tree

16 files changed

+1397
-1
lines changed

16 files changed

+1397
-1
lines changed

src/core/pal/feature.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList<LabelPosit
683683
straightSegmentLengths << currentStraightSegmentLength;
684684
straightSegmentAngles << QgsGeometryUtils::normalizedAngle( atan2( y[numberNodes-1] - segmentStartY, x[numberNodes-1] - segmentStartX ) );
685685
longestSegmentLength = qMax( longestSegmentLength, currentStraightSegmentLength );
686+
double middleOfLine = totalLineLength / 2.0;
686687

687688
if ( totalLineLength < labelWidth )
688689
{
@@ -729,6 +730,10 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList<LabelPosit
729730

730731
candidateLength = sqrt(( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
731732

733+
734+
// LOTS OF DIFFERENT COSTS TO BALANCE HERE - feel free to tweak these, but please add a unit test
735+
// which covers the situation you are adjusting for (eg "given equal length lines, choose the more horizontal line")
736+
732737
cost = candidateLength / labelWidth;
733738
if ( cost > 0.98 )
734739
cost = 0.0001;
@@ -738,10 +743,15 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList<LabelPosit
738743
cost = ( 1 - cost ) / 100; // ranges from 0.0001 to 0.01 (however a cost 0.005 is already a lot!)
739744
}
740745

741-
// penalize positions which are further from the line's midpoint
746+
// penalize positions which are further from the straight segments's midpoint
742747
double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
743748
double costCenter = 2 * qAbs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment ); // 0 -> 1
744749
cost += costCenter * 0.0005; // < 0, 0.0005 >
750+
751+
// penalize positions which are further from absolute center of whole linestring
752+
double costLineCenter = 2 * qAbs( labelCenter - middleOfLine ) / totalLineLength; // 0 -> 1
753+
cost += costLineCenter * 0.0005; // < 0, 0.0005 >
754+
745755
cost += segmentCost * 0.0005; // prefer labels on longer straight segments
746756
cost += segmentAngleCost * 0.0001; // prefer more horizontal segments, but this is less important than length considerations
747757

tests/src/python/test_qgspallabeling_placement.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ def test_polygon_placement_perimeter(self):
276276
self.layer = TestQgsPalLabeling.loadFeatureLayer('polygon_perimeter')
277277
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
278278
self.lyr.placement = QgsPalLayerSettings.Line
279+
self.lyr.placementFlags = QgsPalLayerSettings.AboveLine
279280
self.checkTest()
280281
self.removeMapLayer(self.layer)
281282
self.layer = None
@@ -385,6 +386,51 @@ def test_prefer_line_below_instead_of_online(self):
385386
self.removeMapLayer(self.layer)
386387
self.layer = None
387388

389+
def test_prefer_longer_lines_over_shorter(self):
390+
# Test that labeling a line using parallel labels will tend to place the labels over the longer straight parts of
391+
# the line
392+
self.layer = TestQgsPalLabeling.loadFeatureLayer('line_placement_1')
393+
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
394+
self.lyr.placement = QgsPalLayerSettings.Line
395+
self.checkTest()
396+
self.removeMapLayer(self.layer)
397+
self.layer = None
398+
399+
def test_prefer_more_horizontal_lines(self):
400+
# Test that labeling a line using parallel labels will tend to place the labels over more horizontal sections
401+
self.layer = TestQgsPalLabeling.loadFeatureLayer('line_placement_2')
402+
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
403+
self.lyr.placement = QgsPalLayerSettings.Line
404+
self.checkTest()
405+
self.removeMapLayer(self.layer)
406+
self.layer = None
407+
408+
def test_label_line_over_small_angles(self):
409+
# Test that labeling a line using parallel labels will place labels near center of straightish line
410+
self.layer = TestQgsPalLabeling.loadFeatureLayer('line_placement_3')
411+
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
412+
self.lyr.placement = QgsPalLayerSettings.Line
413+
self.checkTest()
414+
self.removeMapLayer(self.layer)
415+
self.layer = None
416+
417+
def test_label_line_toward_center(self):
418+
# Test that labeling a line using parallel labels will try to place labels as close to center of line as possible
419+
self.layer = TestQgsPalLabeling.loadFeatureLayer('line_placement_4')
420+
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
421+
self.lyr.placement = QgsPalLayerSettings.Line
422+
self.checkTest()
423+
self.removeMapLayer(self.layer)
424+
self.layer = None
425+
426+
def test_label_line_avoid_jaggy(self):
427+
# Test that labeling a line using parallel labels won't place labels over jaggy bits of line
428+
self.layer = TestQgsPalLabeling.loadFeatureLayer('line_placement_5')
429+
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
430+
self.lyr.placement = QgsPalLayerSettings.Line
431+
self.checkTest()
432+
self.removeMapLayer(self.layer)
433+
self.layer = None
388434

389435
if __name__ == '__main__':
390436
# NOTE: unless PAL_SUITE env var is set all test class methods will be run
4.9 KB
Loading
3.16 KB
Loading
2.43 KB
Loading
11 Bytes
Loading
Loading
Loading
2.66 KB
Loading
4.07 KB
Loading

0 commit comments

Comments
 (0)