Skip to content

Commit

Permalink
[FEATURE] Label polygons using curved labels along perimeter
Browse files Browse the repository at this point in the history
This adds a new mode for labeling polygons, where the perimeter
of the polygon is labeled using curved labeling.

(cherry-picked from 5f33991)
  • Loading branch information
nyalldawson committed Aug 9, 2016
1 parent 8c6f5fd commit 5ab62a9
Show file tree
Hide file tree
Showing 6 changed files with 548 additions and 93 deletions.
31 changes: 23 additions & 8 deletions src/app/qgslabelinggui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
mPlacePolygonBtnGrp->addButton( radPolygonHorizontal, ( int )QgsPalLayerSettings::Horizontal );
mPlacePolygonBtnGrp->addButton( radPolygonFree, ( int )QgsPalLayerSettings::Free );
mPlacePolygonBtnGrp->addButton( radPolygonPerimeter, ( int )QgsPalLayerSettings::Line );
mPlacePolygonBtnGrp->addButton( radPolygonPerimeterCurved, ( int )QgsPalLayerSettings::PerimeterCurved );
mPlacePolygonBtnGrp->setExclusive( true );
connect( mPlacePolygonBtnGrp, SIGNAL( buttonClicked( int ) ), this, SLOT( updatePlacementWidgets() ) );

Expand Down Expand Up @@ -465,6 +466,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
<< radPolygonFree
<< radPolygonHorizontal
<< radPolygonPerimeter
<< radPolygonPerimeterCurved
<< radPredefinedOrder
<< mFieldExpressionWidget;
connectValueChanged( widgets, SLOT( updatePreview() ) );
Expand Down Expand Up @@ -672,6 +674,9 @@ void QgsLabelingGui::init()
case QgsPalLayerSettings::Free:
radPolygonFree->setChecked( true );
break;
case QgsPalLayerSettings::PerimeterCurved:
radPolygonPerimeterCurved->setChecked( true );
break;
}

// Label repeat distance
Expand Down Expand Up @@ -962,11 +967,17 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.placement = QgsPalLayerSettings::OrderedPositionsAroundPoint;
}
else if (( curPlacementWdgt == pageLine && radLineParallel->isChecked() )
|| ( curPlacementWdgt == pagePolygon && radPolygonPerimeter->isChecked() )
|| ( curPlacementWdgt == pageLine && radLineCurved->isChecked() ) )
|| ( curPlacementWdgt == pagePolygon && radPolygonPerimeter->isChecked() ) )
{
lyr.placement = QgsPalLayerSettings::Line;
}
else if ( curPlacementWdgt == pageLine && radLineCurved->isChecked() )
{
lyr.placement = QgsPalLayerSettings::Curved;
}
else if ( curPlacementWdgt == pagePolygon && radPolygonPerimeterCurved->isChecked() )
{
bool curved = ( curPlacementWdgt == pageLine && radLineCurved->isChecked() );
lyr.placement = ( curved ? QgsPalLayerSettings::Curved : QgsPalLayerSettings::Line );
lyr.placement = QgsPalLayerSettings::PerimeterCurved;
}
else if (( curPlacementWdgt == pageLine && radLineHorizontal->isChecked() )
|| ( curPlacementWdgt == pagePolygon && radPolygonHorizontal->isChecked() ) )
Expand Down Expand Up @@ -1707,7 +1718,8 @@ void QgsLabelingGui::updatePlacementWidgets()
}
else if (( curWdgt == pageLine && radLineParallel->isChecked() )
|| ( curWdgt == pagePolygon && radPolygonPerimeter->isChecked() )
|| ( curWdgt == pageLine && radLineCurved->isChecked() ) )
|| ( curWdgt == pageLine && radLineCurved->isChecked() )
|| ( curWdgt == pagePolygon && radPolygonPerimeterCurved->isChecked() ) )
{
showLineFrame = true;
showDistanceFrame = true;
Expand All @@ -1717,9 +1729,11 @@ void QgsLabelingGui::updatePlacementWidgets()
chkLineOrientationDependent->setEnabled( offline );
mPlacementDistanceFrame->setEnabled( offline );

showMaxCharAngleFrame = ( curWdgt == pageLine && radLineCurved->isChecked() );
bool isCurved = ( curWdgt == pageLine && radLineCurved->isChecked() )
|| ( curWdgt == pagePolygon && radPolygonPerimeterCurved->isChecked() );
showMaxCharAngleFrame = isCurved;
// TODO: enable mMultiLinesFrame when supported for curved labels
enableMultiLinesFrame = !( curWdgt == pageLine && radLineCurved->isChecked() );
enableMultiLinesFrame = !isCurved;
}

mPlacementLineFrame->setVisible( showLineFrame );
Expand All @@ -1731,7 +1745,8 @@ void QgsLabelingGui::updatePlacementWidgets()
mPlacementDistanceFrame->setVisible( showDistanceFrame );
mPlacementOffsetTypeFrame->setVisible( showOffsetTypeFrame );
mPlacementRotationFrame->setVisible( showRotationFrame );
mPlacementRepeatDistanceFrame->setVisible( curWdgt == pageLine || ( curWdgt == pagePolygon && radPolygonPerimeter->isChecked() ) );
mPlacementRepeatDistanceFrame->setVisible( curWdgt == pageLine || ( curWdgt == pagePolygon &&
( radPolygonPerimeter->isChecked() || radPolygonPerimeterCurved->isChecked() ) ) );
mPlacementMaxCharAngleFrame->setVisible( showMaxCharAngleFrame );

mMultiLinesFrame->setEnabled( enableMultiLinesFrame );
Expand Down
18 changes: 17 additions & 1 deletion src/core/pal/feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1008,10 +1008,21 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
// and the line has right-to-left direction
bool reversed = ( !( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false );

// an orientation of 0 means try both orientations and choose the best
int orientation = 0;
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,
// then we can only accept a single orientation, as we need to ensure that the labels fall
// inside or outside the polygon (and not mixed)
orientation = reversed ? -1 : 1;
}

// generate curved labels
for ( int i = 0; i*delta < total_distance; i++ )
{
LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, 0, 1, i * delta );
LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta );

if ( slp )
{
Expand Down Expand Up @@ -1325,6 +1336,8 @@ int FeaturePart::createCandidates( QList< LabelPosition*>& lPos,
case GEOS_LINESTRING:
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Curved )
createCurvedCandidatesAlongLine( lPos, mapShape );
else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::PerimeterCurved )
createCurvedCandidatesAlongLine( lPos, mapShape );
else
createCandidatesAlongLine( lPos, mapShape );
break;
Expand All @@ -1344,6 +1357,9 @@ int FeaturePart::createCandidates( QList< LabelPosition*>& lPos,
case QgsPalLayerSettings::Line:
createCandidatesAlongLine( lPos, mapShape );
break;
case QgsPalLayerSettings::PerimeterCurved:
createCurvedCandidatesAlongLine( lPos, mapShape );
break;
default:
createCandidatesForPolygon( lPos, mapShape );
break;
Expand Down
1 change: 1 addition & 0 deletions src/core/pal/labelposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h,

// upside down ? (curved labels are always correct)
if ( feature->layer()->arrangement() != QgsPalLayerSettings::Curved &&
feature->layer()->arrangement() != QgsPalLayerSettings::PerimeterCurved &&
this->alpha > M_PI / 2 && this->alpha <= 3*M_PI / 2 )
{
bool uprightLabel = false;
Expand Down
7 changes: 4 additions & 3 deletions src/core/qgspallabeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2374,7 +2374,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
double maxcharanglein = 20.0; // range 20.0-60.0
double maxcharangleout = -20.0; // range 20.0-95.0

if ( placement == QgsPalLayerSettings::Curved )
if ( placement == QgsPalLayerSettings::Curved || placement == QgsPalLayerSettings::PerimeterCurved )
{
maxcharanglein = maxCurvedCharAngleIn;
maxcharangleout = maxCurvedCharAngleOut;
Expand Down Expand Up @@ -2518,7 +2518,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont

GEOSGeometry* geos_geom_clone;
GEOSGeomTypes geomType = ( GEOSGeomTypes ) GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom );
if (( geomType == GEOS_POLYGON || geomType == GEOS_MULTIPOLYGON ) && repeatDistance > 0 && placement == Line )
if (( geomType == GEOS_POLYGON || geomType == GEOS_MULTIPOLYGON ) && repeatDistance > 0 && ( placement == Line || placement == PerimeterCurved ) )
{
geos_geom_clone = GEOSBoundary_r( QgsGeometry::getGEOSHandler(), geos_geom );
}
Expand Down Expand Up @@ -2879,7 +2879,8 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont

// TODO: only for placement which needs character info
// account for any data defined font metrics adjustments
lf->calculateInfo( placement == QgsPalLayerSettings::Curved, labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
lf->calculateInfo( placement == QgsPalLayerSettings::Curved || placement == QgsPalLayerSettings::PerimeterCurved,
labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
// for labelFeature the LabelInfo is passed to feat when it is registered

// TODO: allow layer-wide feature dist in PAL...?
Expand Down
1 change: 1 addition & 0 deletions src/core/qgspallabeling.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class CORE_EXPORT QgsPalLayerSettings
Horizontal, /**< Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only.*/
Free, /**< Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the polygon's orientation. Applies to polygon layers only.*/
OrderedPositionsAroundPoint, /**< Candidates are placed in predefined positions around a point. Peference is given to positions with greatest cartographic appeal, eg top right, bottom right, etc. Applies to point layers only.*/
PerimeterCurved, /** Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.*/
};

//! Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode
Expand Down
Loading

0 comments on commit 5ab62a9

Please sign in to comment.