From e304662a4f466ff0590f399000289b537f7c5bb2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 24 Jul 2017 10:34:59 +1000 Subject: [PATCH] Use standard QgsUnitTypes::RenderUnit throughout labeling Instead of duplicate labeling specific unit enum, reuse the QgsUnitTypes::RenderUnit enum in labeling. This brings several improvements, including: - label offset/distance/repeat units now works correctly in all available unit types (inc pixels, map unit meters, points, inches, etc) - less duplicate code - labeling can use the robust QgsRenderContext methods for converting between different units and painter coordinates Also change comments for members to doxygen comments, so that these get included in the API docs. --- python/core/qgspallabeling.sip | 287 +++++++++++++++++++--- src/app/qgslabelinggui.cpp | 12 +- src/core/qgspallabeling.cpp | 156 +++++++----- src/core/qgspallabeling.h | 299 +++++++++++++++++++---- tests/src/core/testqgslabelingengine.cpp | 1 - 5 files changed, 607 insertions(+), 148 deletions(-) diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip index 4da69383435a..40db812ab7ba 100644 --- a/python/core/qgspallabeling.sip +++ b/python/core/qgspallabeling.sip @@ -136,15 +136,6 @@ class QgsPalLayerSettings PolygonWhole }; - - enum SizeUnit - { - Points, - MM, - MapUnits, - Percent - }; - enum Property { // text @@ -282,15 +273,21 @@ class QgsPalLayerSettings QString fieldName; +%Docstring + Name of field (or an expression) to use for label text. + If fieldName is an expression, then isExpression should be set to true. +.. seealso:: isExpression +%End bool isExpression; %Docstring - Is this label made from a expression string, e.g., FieldName || 'mm' + True if this label is made from a expression string, e.g., FieldName || 'mm' +.. seealso:: fieldName %End QgsExpression *getLabelExpression(); %Docstring - Returns the QgsExpression for this label settings. + Returns the QgsExpression for this label settings. May be None if isExpression is false. :rtype: QgsExpression %End @@ -307,70 +304,228 @@ True if substitutions should be applied QString wrapChar; - MultiLineAlign multilineAlign; // horizontal alignment of multi-line labels +%Docstring + Wrapping character string. If set, any occurrences of this string in the calculated + label text will be replaced with new line characters. +%End + + MultiLineAlign multilineAlign; +%Docstring +Horizontal alignment of multi-line labels. +%End bool addDirectionSymbol; +%Docstring + If true, '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) + will be automatically added to the label text, pointing in the + direction of the line or polygon ring. + This setting only affects line or perimeter based labels. +.. seealso:: leftDirectionSymbol +.. seealso:: rightDirectionSymbol +.. seealso:: placeDirectionSymbol +.. seealso:: reverseDirectionSymbol +%End + QString leftDirectionSymbol; +%Docstring + String to use for left direction arrows. +.. seealso:: addDirectionSymbol +.. seealso:: rightDirectionSymbol +%End + QString rightDirectionSymbol; - DirectionSymbols placeDirectionSymbol; // whether to place left/right, above or below label +%Docstring + String to use for right direction arrows. +.. seealso:: addDirectionSymbol +.. seealso:: leftDirectionSymbol +%End + + DirectionSymbols placeDirectionSymbol; +%Docstring + Placement option for direction symbols. Controls whether to place symbols to the left/right, above or below label. +.. seealso:: addDirectionSymbol +%End + bool reverseDirectionSymbol; +%Docstring +True if direction symbols should be reversed +%End bool formatNumbers; +%Docstring + Set to true to format numeric label text as numbers (e.g. inserting thousand separators + and fixed number of decimal places). +.. seealso:: decimals +.. seealso:: plusSign +%End + int decimals; +%Docstring + Number of decimal places to show for numeric labels. formatNumbers must be true for this + setting to have an effect. +.. seealso:: formatNumbers +%End + bool plusSign; +%Docstring + Whether '+' signs should be prepended to positive numeric labels. formatNumbers must be true for this + setting to have an effect. +.. seealso:: formatNumbers +%End Placement placement; unsigned int placementFlags; - bool centroidWhole; // whether centroid calculated from whole or visible polygon - bool centroidInside; // whether centroid-point calculated must be inside polygon + bool centroidWhole; +%Docstring + True if feature centroid should be calculated from the whole feature, or + false if only the visible part of the feature should be considered. +%End + + bool centroidInside; +%Docstring + True if centroid positioned labels must be placed inside their corresponding + feature polygon, or false if centroids which fall outside the polygon + are permitted. +%End bool fitInPolygonOnly; %Docstring True if only labels which completely fit within a polygon are allowed. %End - double dist; // distance from the feature (in mm) - bool distInMapUnits; //true if distance is in map units (otherwise in mm) + + double dist; +%Docstring + Distance from feature to the label. Units are specified via distUnits. +.. seealso:: distUnits +.. seealso:: distMapUnitScale +%End + + QgsUnitTypes::RenderUnit distUnits; +%Docstring + Units the distance from feature to the label. +.. seealso:: dist +.. seealso:: distMapUnitScale +%End + QgsMapUnitScale distMapUnitScale; +%Docstring + Map unit scale for label feature distance. +.. seealso:: dist +.. seealso:: distUnits +%End + OffsetType offsetType; %Docstring Offset type for layer (only applies in certain placement modes) %End double repeatDistance; - SizeUnit repeatDistanceUnit; +%Docstring + Distance for repeating labels for a single feature. +.. seealso:: repeatDistanceUnit +.. seealso:: repeatDistanceMapUnitScale +%End + + QgsUnitTypes::RenderUnit repeatDistanceUnit; +%Docstring + Units for repeating labels for a single feature. +.. seealso:: repeatDistance +.. seealso:: repeatDistanceMapUnitScale +%End + QgsMapUnitScale repeatDistanceMapUnitScale; +%Docstring + Map unit scale for repeating labels for a single feature. +.. seealso:: repeatDistance +.. seealso:: repeatDistanceUnit +%End QuadrantPosition quadOffset; +%Docstring + Sets the quadrant in which to offset labels from feature. +%End + + double xOffset; +%Docstring + Horizontal offset of label. Units are specified via offsetUnits. +.. seealso:: yOffset +.. seealso:: offsetUnits +.. seealso:: labelOffsetMapUnitScale +%End + + double yOffset; +%Docstring + Vertical offset of label. Units are specified via offsetUnits. +.. seealso:: xOffset +.. seealso:: offsetUnits +.. seealso:: labelOffsetMapUnitScale +%End + + QgsUnitTypes::RenderUnit offsetUnits; +%Docstring + Units for offsets of label. +.. seealso:: xOffset +.. seealso:: yOffset +.. seealso:: labelOffsetMapUnitScale +%End - double xOffset; // offset from point in mm or map units - double yOffset; // offset from point in mm or map units - bool labelOffsetInMapUnits; //true if label offset is in map units (otherwise in mm) QgsMapUnitScale labelOffsetMapUnitScale; +%Docstring + Map unit scale for label offset. +.. seealso:: xOffset +.. seealso:: yOffset +.. seealso:: offsetUnits +%End double angleOffset; %Docstring Label rotation, in degrees clockwise %End - bool preserveRotation; // preserve predefined rotation data during label pin/unpin operations + bool preserveRotation; +%Docstring +True if label rotation should be preserved during label pin/unpin operations. +%End - double maxCurvedCharAngleIn; // maximum angle between inside curved label characters (defaults to 20.0, range 20.0 to 60.0) - double maxCurvedCharAngleOut; // maximum angle between outside curved label characters (defaults to -20.0, range -20.0 to -95.0) + double maxCurvedCharAngleIn; +%Docstring + Maximum angle between inside curved label characters (valid range 20.0 to 60.0). +.. seealso:: maxCurvedCharAngleOut +%End - int priority; // 0 = low, 10 = high + double maxCurvedCharAngleOut; +%Docstring + Maximum angle between outside curved label characters (valid range -20.0 to -95.0) +.. seealso:: maxCurvedCharAngleIn +%End + + int priority; +%Docstring + Label priority. Valid ranges are from 0 to 10, where 0 = lowest priority + and 10 = highest priority. +%End bool scaleVisibility; +%Docstring + Set to true to limit label visibility to a range of scales. +.. seealso:: maximumScale +.. seealso:: minimumScale +%End double maximumScale; %Docstring The maximum map scale (i.e. most "zoomed in" scale) at which the labels will be visible. The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. A scale of 0 indicates no maximum scale visibility. + + This setting is only considered if scaleVisibility is true. + .. seealso:: minimumScale +.. seealso:: scaleVisibility %End double minimumScale; @@ -378,34 +533,94 @@ Label rotation, in degrees clockwise The minimum map scale (i.e. most "zoomed out" scale) at which the labels will be visible. The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. A scale of 0 indicates no minimum scale visibility. + + This setting is only considered if scaleVisibility is true. + .. seealso:: maximumScale +.. seealso:: scaleVisibility %End - bool fontLimitPixelSize; // true is label should be limited by fontMinPixelSize/fontMaxPixelSize - int fontMinPixelSize; // minimum pixel size for showing rendered map unit labels (1 - 1000) - int fontMaxPixelSize; // maximum pixel size for showing rendered map unit labels (1 - 10000) + bool fontLimitPixelSize; +%Docstring + True if label sizes should be limited by pixel size. +.. seealso:: fontMinPixelSize +.. seealso:: fontMaxPixelSize +%End + + int fontMinPixelSize; +%Docstring + Minimum pixel size for showing rendered map unit labels (1 - 1000). +.. seealso:: fontLimitPixelSize +.. seealso:: fontMaxPixelSize +%End + + int fontMaxPixelSize; +%Docstring + Maximum pixel size for showing rendered map unit labels (1 - 10000). +.. seealso:: fontLimitPixelSize +.. seealso:: fontMinPixelSize +%End + + bool displayAll; +%Docstring +If true, all features will be labelled even when overlaps occur. +%End + + UpsideDownLabels upsidedownLabels; +%Docstring +Controls whether upside down labels are displayed and how they are handled. +%End - bool displayAll; // if true, all features will be labelled even though overlaps occur - UpsideDownLabels upsidedownLabels; // whether, or how, to show upsidedown labels + bool labelPerPart; +%Docstring + True if every part of a multi-part feature should be labeled. If false, + only the largest part will be labeled. +%End - bool labelPerPart; // whether to label every feature's part or only the biggest one bool mergeLines; +%Docstring + True if connected line features with identical label text should be merged + prior to generating label positions. +%End + + bool limitNumLabels; +%Docstring + True if the number of labels drawn should be limited. +.. seealso:: maxNumLabels +%End - bool limitNumLabels; // whether to limit the number of labels to be drawn - int maxNumLabels; // maximum number of labels to be drawn + int maxNumLabels; +%Docstring + The maximum number of labels which should be drawn for this layer. + This only has an effect if limitNumLabels is true. +.. seealso:: limitNumLabels +%End - double minFeatureSize; // minimum feature size to be labelled (in mm) - bool obstacle; // whether features for layer are obstacles to labels of other layers + double minFeatureSize; +%Docstring + Minimum feature size (in millimeters) for a feature to be labelled. +%End + + bool obstacle; +%Docstring + True if features for layer are obstacles to labels of other layers. +.. seealso:: obstacleFactor +.. seealso:: obstacleType +%End double obstacleFactor; %Docstring Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels, > 1.0 less likely to be covered +.. seealso:: obstacle +.. seealso:: obstacleType %End ObstacleType obstacleType; %Docstring - Controls how features act as obstacles for labels + Controls how features act as obstacles for labels. +.. seealso:: obstacle +.. seealso:: obstacleFactor %End double zIndex; diff --git a/src/app/qgslabelinggui.cpp b/src/app/qgslabelinggui.cpp index 46d79faf5f85..1d7441c862e5 100644 --- a/src/app/qgslabelinggui.cpp +++ b/src/app/qgslabelinggui.cpp @@ -160,13 +160,13 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer ) mCentroidInsideCheckBox->setChecked( lyr.centroidInside ); mFitInsidePolygonCheckBox->setChecked( lyr.fitInPolygonOnly ); mLineDistanceSpnBx->setValue( lyr.dist ); - mLineDistanceUnitWidget->setUnit( lyr.distInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters ); + mLineDistanceUnitWidget->setUnit( lyr.distUnits ); mLineDistanceUnitWidget->setMapUnitScale( lyr.distMapUnitScale ); mOffsetTypeComboBox->setCurrentIndex( mOffsetTypeComboBox->findData( lyr.offsetType ) ); mQuadrantBtnGrp->button( ( int )lyr.quadOffset )->setChecked( true ); mPointOffsetXSpinBox->setValue( lyr.xOffset ); mPointOffsetYSpinBox->setValue( lyr.yOffset ); - mPointOffsetUnitWidget->setUnit( lyr.labelOffsetInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters ); + mPointOffsetUnitWidget->setUnit( lyr.offsetUnits ); mPointOffsetUnitWidget->setMapUnitScale( lyr.labelOffsetMapUnitScale ); mPointAngleSpinBox->setValue( lyr.angleOffset ); chkLineAbove->setChecked( lyr.placementFlags & QgsPalLayerSettings::AboveLine ); @@ -209,7 +209,7 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer ) // Label repeat distance mRepeatDistanceSpinBox->setValue( lyr.repeatDistance ); - mRepeatDistanceUnitWidget->setUnit( lyr.repeatDistanceUnit == QgsPalLayerSettings::MapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters ); + mRepeatDistanceUnitWidget->setUnit( lyr.repeatDistanceUnit ); mRepeatDistanceUnitWidget->setMapUnitScale( lyr.repeatDistanceMapUnitScale ); mPrioritySlider->setValue( lyr.priority ); @@ -314,7 +314,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings() lyr.centroidInside = mCentroidInsideCheckBox->isChecked(); lyr.fitInPolygonOnly = mFitInsidePolygonCheckBox->isChecked(); lyr.dist = mLineDistanceSpnBx->value(); - lyr.distInMapUnits = ( mLineDistanceUnitWidget->unit() == QgsUnitTypes::RenderMapUnits ); + lyr.distUnits = mLineDistanceUnitWidget->unit(); lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale(); lyr.offsetType = static_cast< QgsPalLayerSettings::OffsetType >( mOffsetTypeComboBox->currentData().toInt() ); if ( mQuadrantBtnGrp ) @@ -323,7 +323,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings() } lyr.xOffset = mPointOffsetXSpinBox->value(); lyr.yOffset = mPointOffsetYSpinBox->value(); - lyr.labelOffsetInMapUnits = ( mPointOffsetUnitWidget->unit() == QgsUnitTypes::RenderMapUnits ); + lyr.offsetUnits = mPointOffsetUnitWidget->unit(); lyr.labelOffsetMapUnitScale = mPointOffsetUnitWidget->getMapUnitScale(); lyr.angleOffset = mPointAngleSpinBox->value(); if ( chkLineAbove->isChecked() ) @@ -376,7 +376,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings() } lyr.repeatDistance = mRepeatDistanceSpinBox->value(); - lyr.repeatDistanceUnit = mRepeatDistanceUnitWidget->unit() == QgsUnitTypes::RenderMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::MM; + lyr.repeatDistanceUnit = mRepeatDistanceUnitWidget->unit(); lyr.repeatDistanceMapUnitScale = mRepeatDistanceUnitWidget->getMapUnitScale(); lyr.previewBkgrdColor = mPreviewBackgroundBtn->color(); diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 4f2b7001f9b6..989506e0db89 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -269,9 +269,9 @@ QgsPalLayerSettings::QgsPalLayerSettings() quadOffset = QuadrantOver; xOffset = 0; yOffset = 0; - labelOffsetInMapUnits = true; + offsetUnits = QgsUnitTypes::RenderMillimeters; dist = 0; - distInMapUnits = false; + distUnits = QgsUnitTypes::RenderMillimeters; offsetType = FromPoint; angleOffset = 0; preserveRotation = true; @@ -279,7 +279,7 @@ QgsPalLayerSettings::QgsPalLayerSettings() maxCurvedCharAngleOut = -25.0; priority = 5; repeatDistance = 0; - repeatDistanceUnit = MM; + repeatDistanceUnit = QgsUnitTypes::RenderMillimeters; // rendering scaleVisibility = false; @@ -354,11 +354,11 @@ QgsPalLayerSettings &QgsPalLayerSettings::operator=( const QgsPalLayerSettings & quadOffset = s.quadOffset; xOffset = s.xOffset; yOffset = s.yOffset; - labelOffsetInMapUnits = s.labelOffsetInMapUnits; + offsetUnits = s.offsetUnits; labelOffsetMapUnitScale = s.labelOffsetMapUnitScale; dist = s.dist; offsetType = s.offsetType; - distInMapUnits = s.distInMapUnits; + distUnits = s.distUnits; distMapUnitScale = s.distMapUnitScale; angleOffset = s.angleOffset; preserveRotation = s.preserveRotation; @@ -419,16 +419,6 @@ QgsExpression *QgsPalLayerSettings::getLabelExpression() return expression; } -static QgsPalLayerSettings::SizeUnit _decodeUnits( const QString &str ) -{ - if ( str.compare( QLatin1String( "Point" ), Qt::CaseInsensitive ) == 0 - || str.compare( QLatin1String( "Points" ), Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points; - if ( str.compare( QLatin1String( "MapUnit" ), Qt::CaseInsensitive ) == 0 - || str.compare( QLatin1String( "MapUnits" ), Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits; - if ( str.compare( QLatin1String( "Percent" ), Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent; - return QgsPalLayerSettings::MM; // "MM" -} - static Qt::PenJoinStyle _decodePenJoinStyle( const QString &str ) { if ( str.compare( QLatin1String( "Miter" ), Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin; @@ -575,7 +565,7 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer ) predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER; fitInPolygonOnly = layer->customProperty( QStringLiteral( "labeling/fitInPolygonOnly" ), QVariant( false ) ).toBool(); dist = layer->customProperty( QStringLiteral( "labeling/dist" ) ).toDouble(); - distInMapUnits = layer->customProperty( QStringLiteral( "labeling/distInMapUnits" ) ).toBool(); + distUnits = layer->customProperty( QStringLiteral( "labeling/distInMapUnits" ) ).toBool() ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters; if ( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString().isEmpty() ) { //fallback to older property @@ -592,7 +582,11 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer ) quadOffset = static_cast< QuadrantPosition >( layer->customProperty( QStringLiteral( "labeling/quadOffset" ), QVariant( QuadrantOver ) ).toUInt() ); xOffset = layer->customProperty( QStringLiteral( "labeling/xOffset" ), QVariant( 0.0 ) ).toDouble(); yOffset = layer->customProperty( QStringLiteral( "labeling/yOffset" ), QVariant( 0.0 ) ).toDouble(); - labelOffsetInMapUnits = layer->customProperty( QStringLiteral( "labeling/labelOffsetInMapUnits" ), QVariant( true ) ).toBool(); + if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetInMapUnits" ), QVariant( true ) ).toBool() ) + offsetUnits = QgsUnitTypes::RenderMapUnits; + else + offsetUnits = QgsUnitTypes::RenderMillimeters; + if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() ) { //fallback to older property @@ -622,7 +616,21 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer ) maxCurvedCharAngleOut = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleOut" ), QVariant( -25.0 ) ).toDouble(); priority = layer->customProperty( QStringLiteral( "labeling/priority" ) ).toInt(); repeatDistance = layer->customProperty( QStringLiteral( "labeling/repeatDistance" ), 0.0 ).toDouble(); - repeatDistanceUnit = static_cast< SizeUnit >( layer->customProperty( QStringLiteral( "labeling/repeatDistanceUnit" ), QVariant( MM ) ).toUInt() ); + switch ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceUnit" ), QVariant( 1 ) ).toUInt() ) + { + case 0: + repeatDistanceUnit = QgsUnitTypes::RenderPoints; + break; + case 1: + repeatDistanceUnit = QgsUnitTypes::RenderMillimeters; + break; + case 2: + repeatDistanceUnit = QgsUnitTypes::RenderMapUnits; + break; + case 3: + repeatDistanceUnit = QgsUnitTypes::RenderPercentage; + break; + } if ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() ) { //fallback to older property @@ -765,7 +773,17 @@ void QgsPalLayerSettings::readXml( QDomElement &elem, const QgsReadWriteContext predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER; fitInPolygonOnly = placementElem.attribute( QStringLiteral( "fitInPolygonOnly" ), QStringLiteral( "0" ) ).toInt(); dist = placementElem.attribute( QStringLiteral( "dist" ) ).toDouble(); - distInMapUnits = placementElem.attribute( QStringLiteral( "distInMapUnits" ) ).toInt(); + if ( !placementElem.hasAttribute( QStringLiteral( "distUnits" ) ) ) + { + if ( placementElem.attribute( QStringLiteral( "distInMapUnits" ) ).toInt() ) + distUnits = QgsUnitTypes::RenderMapUnits; + else + distUnits = QgsUnitTypes::RenderMillimeters; + } + else + { + distUnits = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "distUnits" ) ) ); + } if ( !placementElem.hasAttribute( QStringLiteral( "distMapUnitScale" ) ) ) { //fallback to older property @@ -782,7 +800,14 @@ void QgsPalLayerSettings::readXml( QDomElement &elem, const QgsReadWriteContext quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( QStringLiteral( "quadOffset" ), QString::number( QuadrantOver ) ).toUInt() ); xOffset = placementElem.attribute( QStringLiteral( "xOffset" ), QStringLiteral( "0" ) ).toDouble(); yOffset = placementElem.attribute( QStringLiteral( "yOffset" ), QStringLiteral( "0" ) ).toDouble(); - labelOffsetInMapUnits = placementElem.attribute( QStringLiteral( "labelOffsetInMapUnits" ), QStringLiteral( "1" ) ).toInt(); + if ( !placementElem.hasAttribute( QStringLiteral( "offsetUnits" ) ) ) + { + offsetUnits = placementElem.attribute( QStringLiteral( "labelOffsetInMapUnits" ), QStringLiteral( "1" ) ).toInt() ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters; + } + else + { + offsetUnits = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "offsetUnits" ) ) ); + } if ( !placementElem.hasAttribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) ) { //fallback to older property @@ -811,7 +836,29 @@ void QgsPalLayerSettings::readXml( QDomElement &elem, const QgsReadWriteContext maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleOut" ), QStringLiteral( "-25" ) ).toDouble(); priority = placementElem.attribute( QStringLiteral( "priority" ) ).toInt(); repeatDistance = placementElem.attribute( QStringLiteral( "repeatDistance" ), QStringLiteral( "0" ) ).toDouble(); - repeatDistanceUnit = static_cast< SizeUnit >( placementElem.attribute( QStringLiteral( "repeatDistanceUnit" ), QString::number( MM ) ).toUInt() ); + if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceUnits" ) ) ) + { + // upgrade old setting + switch ( placementElem.attribute( QStringLiteral( "repeatDistanceUnit" ), QString::number( 1 ) ).toUInt() ) + { + case 0: + repeatDistanceUnit = QgsUnitTypes::RenderPoints; + break; + case 1: + repeatDistanceUnit = QgsUnitTypes::RenderMillimeters; + break; + case 2: + repeatDistanceUnit = QgsUnitTypes::RenderMapUnits; + break; + case 3: + repeatDistanceUnit = QgsUnitTypes::RenderPercentage; + break; + } + } + else + { + repeatDistanceUnit = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "repeatDistanceUnits" ) ) ); + } if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) ) { //fallback to older property @@ -938,13 +985,13 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite placementElem.setAttribute( QStringLiteral( "predefinedPositionOrder" ), QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) ); placementElem.setAttribute( QStringLiteral( "fitInPolygonOnly" ), fitInPolygonOnly ); placementElem.setAttribute( QStringLiteral( "dist" ), dist ); - placementElem.setAttribute( QStringLiteral( "distInMapUnits" ), distInMapUnits ); + placementElem.setAttribute( QStringLiteral( "distUnits" ), QgsUnitTypes::encodeUnit( distUnits ) ); placementElem.setAttribute( QStringLiteral( "distMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( distMapUnitScale ) ); placementElem.setAttribute( QStringLiteral( "offsetType" ), static_cast< unsigned int >( offsetType ) ); placementElem.setAttribute( QStringLiteral( "quadOffset" ), static_cast< unsigned int >( quadOffset ) ); placementElem.setAttribute( QStringLiteral( "xOffset" ), xOffset ); placementElem.setAttribute( QStringLiteral( "yOffset" ), yOffset ); - placementElem.setAttribute( QStringLiteral( "labelOffsetInMapUnits" ), labelOffsetInMapUnits ); + placementElem.setAttribute( QStringLiteral( "offsetUnits" ), QgsUnitTypes::encodeUnit( offsetUnits ) ); placementElem.setAttribute( QStringLiteral( "labelOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( labelOffsetMapUnitScale ) ); placementElem.setAttribute( QStringLiteral( "rotationAngle" ), angleOffset ); placementElem.setAttribute( QStringLiteral( "preserveRotation" ), preserveRotation ); @@ -952,7 +999,7 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleOut" ), maxCurvedCharAngleOut ); placementElem.setAttribute( QStringLiteral( "priority" ), priority ); placementElem.setAttribute( QStringLiteral( "repeatDistance" ), repeatDistance ); - placementElem.setAttribute( QStringLiteral( "repeatDistanceUnit" ), repeatDistanceUnit ); + placementElem.setAttribute( QStringLiteral( "repeatDistanceUnits" ), QgsUnitTypes::encodeUnit( repeatDistanceUnit ) ); placementElem.setAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( repeatDistanceMapUnitScale ) ); // rendering @@ -1605,7 +1652,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont } // data defined label offset units? - bool offinmapunits = labelOffsetInMapUnits; + QgsUnitTypes::RenderUnit offUnit = offsetUnits; exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetUnits, context.expressionContext() ); if ( exprVal.isValid() ) { @@ -1613,29 +1660,20 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 ); if ( !units.isEmpty() ) { - offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits ); + bool ok = false; + QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok ); + if ( ok ) + { + offUnit = decodedUnits; + } } } // adjust offset of labels to match chosen unit and map scale // offsets match those of symbology: -x = left, -y = up - double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor(); - if ( !qgsDoubleNear( xOff, 0.0 ) ) - { - offsetX = xOff; // must be positive to match symbology offset direction - if ( !offinmapunits ) - { - offsetX *= mapUntsPerMM; //convert offset from mm to map units - } - } - if ( !qgsDoubleNear( yOff, 0.0 ) ) - { - offsetY = -yOff; // must be negative to match symbology offset direction - if ( !offinmapunits ) - { - offsetY *= mapUntsPerMM; //convert offset from mm to map units - } - } + offsetX = context.convertToMapUnits( xOff, offUnit, labelOffsetMapUnitScale ); + // must be negative to match symbology offset direction + offsetY = context.convertToMapUnits( -yOff, offUnit, labelOffsetMapUnitScale ); // layer defined rotation? // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature @@ -1782,7 +1820,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont double repeatDist = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::RepeatDistance, context.expressionContext(), repeatDistance ); // data defined label-repeat distance units? - bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits; + QgsUnitTypes::RenderUnit repeatUnits = repeatDistanceUnit; exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::RepeatDistanceUnit, context.expressionContext() ); if ( exprVal.isValid() ) { @@ -1790,15 +1828,20 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 ); if ( !units.isEmpty() ) { - repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits ); + bool ok = false; + QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok ); + if ( ok ) + { + repeatUnits = decodedUnits; + } } } if ( !qgsDoubleNear( repeatDist, 0.0 ) ) { - if ( !repeatdistinmapunit ) + if ( repeatUnits != QgsUnitTypes::RenderMapUnits ) { - repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units + repeatDist = context.convertToMapUnits( repeatDist, repeatUnits, repeatDistanceMapUnitScale ); } } @@ -1856,7 +1899,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont double distance = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::LabelDistance, context.expressionContext(), dist ); // data defined label-feature distance units? - bool distinmapunit = distInMapUnits; + QgsUnitTypes::RenderUnit distUnit = distUnits; exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::DistanceUnits, context.expressionContext() ); if ( exprVal.isValid() ) { @@ -1864,18 +1907,15 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 ); if ( !units.isEmpty() ) { - distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits ); + bool ok = false; + QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok ); + if ( ok ) + { + distUnit = decodedUnits; + } } } - - if ( distinmapunit ) //convert distance from mm/map units to pixels - { - distance /= distMapUnitScale.computeMapUnitsPerPixel( context ); - } - else //mm - { - distance *= context.scaleFactor(); - } + distance = context.convertToPainterUnits( distance, distUnit, distMapUnitScale ); // when using certain placement modes, we force a tiny minimum distance. This ensures that // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h index 1ddcd22f8445..04d193375b6e 100644 --- a/src/core/qgspallabeling.h +++ b/src/core/qgspallabeling.h @@ -249,16 +249,6 @@ class CORE_EXPORT QgsPalLayerSettings placing labels over any part of the polygon is avoided.*/ }; - - //! Units used for option sizes, before being converted to rendered sizes - enum SizeUnit - { - Points = 0, - MM, - MapUnits, - Percent - }; - //! Data definable properties. enum Property { @@ -395,14 +385,22 @@ class CORE_EXPORT QgsPalLayerSettings //-- text style + /** + * Name of field (or an expression) to use for label text. + * If fieldName is an expression, then isExpression should be set to true. + * \see isExpression + */ QString fieldName; - /** Is this label made from a expression string, e.g., FieldName || 'mm' - */ + /** + * True if this label is made from a expression string, e.g., FieldName || 'mm' + * \see fieldName + */ bool isExpression; - /** Returns the QgsExpression for this label settings. - */ + /** + * Returns the QgsExpression for this label settings. May be nullptr if isExpression is false. + */ QgsExpression *getLabelExpression(); QColor previewBkgrdColor; @@ -414,20 +412,70 @@ class CORE_EXPORT QgsPalLayerSettings //-- text formatting + /** + * Wrapping character string. If set, any occurrences of this string in the calculated + * label text will be replaced with new line characters. + */ QString wrapChar; - MultiLineAlign multilineAlign; // horizontal alignment of multi-line labels - // Adds '<' or '>', or user-defined symbol to the label string pointing to the - // direction of the line / polygon ring - // Works only if Placement == Line + //! Horizontal alignment of multi-line labels. + MultiLineAlign multilineAlign; + + /** + * If true, '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) + * will be automatically added to the label text, pointing in the + * direction of the line or polygon ring. + * This setting only affects line or perimeter based labels. + * \see leftDirectionSymbol + * \see rightDirectionSymbol + * \see placeDirectionSymbol + * \see reverseDirectionSymbol + */ bool addDirectionSymbol; + + /** + * String to use for left direction arrows. + * \see addDirectionSymbol + * \see rightDirectionSymbol + */ QString leftDirectionSymbol; + + /** + * String to use for right direction arrows. + * \see addDirectionSymbol + * \see leftDirectionSymbol + */ QString rightDirectionSymbol; - DirectionSymbols placeDirectionSymbol; // whether to place left/right, above or below label + + /** + * Placement option for direction symbols. Controls whether to place symbols to the left/right, above or below label. + * \see addDirectionSymbol + */ + DirectionSymbols placeDirectionSymbol; + + //! True if direction symbols should be reversed bool reverseDirectionSymbol; + /** + * Set to true to format numeric label text as numbers (e.g. inserting thousand separators + * and fixed number of decimal places). + * \see decimals + * \see plusSign + */ bool formatNumbers; + + /** + * Number of decimal places to show for numeric labels. formatNumbers must be true for this + * setting to have an effect. + * \see formatNumbers + */ int decimals; + + /** + * Whether '+' signs should be prepended to positive numeric labels. formatNumbers must be true for this + * setting to have an effect. + * \see formatNumbers + */ bool plusSign; //-- placement @@ -435,57 +483,156 @@ class CORE_EXPORT QgsPalLayerSettings Placement placement; unsigned int placementFlags; - bool centroidWhole; // whether centroid calculated from whole or visible polygon - bool centroidInside; // whether centroid-point calculated must be inside polygon + /** + * True if feature centroid should be calculated from the whole feature, or + * false if only the visible part of the feature should be considered. + */ + bool centroidWhole; + + /** + * True if centroid positioned labels must be placed inside their corresponding + * feature polygon, or false if centroids which fall outside the polygon + * are permitted. + */ + bool centroidInside; - /** Ordered list of predefined label positions for points. Positions earlier + /** + * Ordered list of predefined label positions for points. Positions earlier * in the list will be prioritized over later positions. Only used when the placement * is set to QgsPalLayerSettings::OrderedPositionsAroundPoint. * \note not available in Python bindings */ QVector< PredefinedPointPosition > predefinedPositionOrder SIP_SKIP; - /** True if only labels which completely fit within a polygon are allowed. + /** + * True if only labels which completely fit within a polygon are allowed. */ bool fitInPolygonOnly; - double dist; // distance from the feature (in mm) - bool distInMapUnits; //true if distance is in map units (otherwise in mm) + + /** + * Distance from feature to the label. Units are specified via distUnits. + * \see distUnits + * \see distMapUnitScale + */ + double dist; + + /** + * Units the distance from feature to the label. + * \see dist + * \see distMapUnitScale + */ + QgsUnitTypes::RenderUnit distUnits; + + /** + * Map unit scale for label feature distance. + * \see dist + * \see distUnits + */ QgsMapUnitScale distMapUnitScale; + //! Offset type for layer (only applies in certain placement modes) OffsetType offsetType; + /** + * Distance for repeating labels for a single feature. + * \see repeatDistanceUnit + * \see repeatDistanceMapUnitScale + */ double repeatDistance; - SizeUnit repeatDistanceUnit; + + /** + * Units for repeating labels for a single feature. + * \see repeatDistance + * \see repeatDistanceMapUnitScale + */ + QgsUnitTypes::RenderUnit repeatDistanceUnit; + + /** + * Map unit scale for repeating labels for a single feature. + * \see repeatDistance + * \see repeatDistanceUnit + */ QgsMapUnitScale repeatDistanceMapUnitScale; - // offset labels of point/centroid features default to center - // move label to quadrant: left/down, don't move, right/up (-1, 0, 1) + /** + * Sets the quadrant in which to offset labels from feature. + */ QuadrantPosition quadOffset; - double xOffset; // offset from point in mm or map units - double yOffset; // offset from point in mm or map units - bool labelOffsetInMapUnits; //true if label offset is in map units (otherwise in mm) + /** + * Horizontal offset of label. Units are specified via offsetUnits. + * \see yOffset + * \see offsetUnits + * \see labelOffsetMapUnitScale + */ + double xOffset; + + /** + * Vertical offset of label. Units are specified via offsetUnits. + * \see xOffset + * \see offsetUnits + * \see labelOffsetMapUnitScale + */ + double yOffset; + + /** + * Units for offsets of label. + * \see xOffset + * \see yOffset + * \see labelOffsetMapUnitScale + */ + QgsUnitTypes::RenderUnit offsetUnits; + + /** + * Map unit scale for label offset. + * \see xOffset + * \see yOffset + * \see offsetUnits + */ QgsMapUnitScale labelOffsetMapUnitScale; //! Label rotation, in degrees clockwise double angleOffset; - bool preserveRotation; // preserve predefined rotation data during label pin/unpin operations + //! True if label rotation should be preserved during label pin/unpin operations. + bool preserveRotation; - double maxCurvedCharAngleIn; // maximum angle between inside curved label characters (defaults to 20.0, range 20.0 to 60.0) - double maxCurvedCharAngleOut; // maximum angle between outside curved label characters (defaults to -20.0, range -20.0 to -95.0) + /** + * Maximum angle between inside curved label characters (valid range 20.0 to 60.0). + * \see maxCurvedCharAngleOut + */ + double maxCurvedCharAngleIn; - int priority; // 0 = low, 10 = high + /** + * Maximum angle between outside curved label characters (valid range -20.0 to -95.0) + * \see maxCurvedCharAngleIn + */ + double maxCurvedCharAngleOut; + + /** + * Label priority. Valid ranges are from 0 to 10, where 0 = lowest priority + * and 10 = highest priority. + */ + int priority; //-- rendering + /** + * Set to true to limit label visibility to a range of scales. + * \see maximumScale + * \see minimumScale + */ bool scaleVisibility; /** * The maximum map scale (i.e. most "zoomed in" scale) at which the labels will be visible. * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. * A scale of 0 indicates no maximum scale visibility. + * + * This setting is only considered if scaleVisibility is true. + * * \see minimumScale + * \see scaleVisibility */ double maximumScale; @@ -493,32 +640,90 @@ class CORE_EXPORT QgsPalLayerSettings * The minimum map scale (i.e. most "zoomed out" scale) at which the labels will be visible. * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. * A scale of 0 indicates no minimum scale visibility. + * + * This setting is only considered if scaleVisibility is true. + * * \see maximumScale + * \see scaleVisibility */ double minimumScale; - bool fontLimitPixelSize; // true is label should be limited by fontMinPixelSize/fontMaxPixelSize - int fontMinPixelSize; // minimum pixel size for showing rendered map unit labels (1 - 1000) - int fontMaxPixelSize; // maximum pixel size for showing rendered map unit labels (1 - 10000) + /** + * True if label sizes should be limited by pixel size. + * \see fontMinPixelSize + * \see fontMaxPixelSize + */ + bool fontLimitPixelSize; + + /** + * Minimum pixel size for showing rendered map unit labels (1 - 1000). + * \see fontLimitPixelSize + * \see fontMaxPixelSize + */ + int fontMinPixelSize; + + /** + * Maximum pixel size for showing rendered map unit labels (1 - 10000). + * \see fontLimitPixelSize + * \see fontMinPixelSize + */ + int fontMaxPixelSize; - bool displayAll; // if true, all features will be labelled even though overlaps occur - UpsideDownLabels upsidedownLabels; // whether, or how, to show upsidedown labels + //! If true, all features will be labelled even when overlaps occur. + bool displayAll; - bool labelPerPart; // whether to label every feature's part or only the biggest one + //! Controls whether upside down labels are displayed and how they are handled. + UpsideDownLabels upsidedownLabels; + + /** + * True if every part of a multi-part feature should be labeled. If false, + * only the largest part will be labeled. + */ + bool labelPerPart; + + /** + * True if connected line features with identical label text should be merged + * prior to generating label positions. + */ bool mergeLines; - bool limitNumLabels; // whether to limit the number of labels to be drawn - int maxNumLabels; // maximum number of labels to be drawn + /** + * True if the number of labels drawn should be limited. + * \see maxNumLabels + */ + bool limitNumLabels; - double minFeatureSize; // minimum feature size to be labelled (in mm) - bool obstacle; // whether features for layer are obstacles to labels of other layers + /** + * The maximum number of labels which should be drawn for this layer. + * This only has an effect if limitNumLabels is true. + * \see limitNumLabels + */ + int maxNumLabels; - /** Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels, + /** + * Minimum feature size (in millimeters) for a feature to be labelled. + */ + double minFeatureSize; + + /** + * True if features for layer are obstacles to labels of other layers. + * \see obstacleFactor + * \see obstacleType + */ + bool obstacle; + + /** + * Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels, * > 1.0 less likely to be covered + * \see obstacle + * \see obstacleType */ double obstacleFactor; - /** Controls how features act as obstacles for labels + /** + * Controls how features act as obstacles for labels. + * \see obstacle + * \see obstacleFactor */ ObstacleType obstacleType; diff --git a/tests/src/core/testqgslabelingengine.cpp b/tests/src/core/testqgslabelingengine.cpp index 02d9730f7968..64270165f2ca 100644 --- a/tests/src/core/testqgslabelingengine.cpp +++ b/tests/src/core/testqgslabelingengine.cpp @@ -216,7 +216,6 @@ void TestQgsLabelingEngine::testRuleBased() s1.fieldName = QStringLiteral( "Class" ); s1.obstacle = false; s1.dist = 2; - s1.distInMapUnits = false; QgsTextFormat format = s1.format(); format.setColor( QColor( 200, 0, 200 ) ); format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ) );