32#include <QApplication>
35#include <QFontMetrics>
40#include <QTextBoundaryFinder>
98 if ( !sPropertyDefinitions()->isEmpty() )
101 const QString origin = QStringLiteral(
"labeling" );
113 "e.g. Helvetica or Helvetica [Cronyx]" ), origin )
117 "e.g. Bold Condensed or Light Italic" ), origin )
158 "<b>Ellipse</b>|<b>Circle</b>|<b>SVG</b>]" ), origin )
182 "<b>Buffer</b>|<b>Background</b>]" ), origin )
198 "<b>3</b>=Left|<b>4</b>=Over|<b>5</b>=Right|<br>"
199 "<b>6</b>=Below Left|<b>7</b>=Below|<b>8</b>=Below Right]" ), origin )
219 + QStringLiteral(
"[<b>TL</b>=Top left|<b>TSL</b>=Top, slightly left|<b>T</b>=Top middle|<br>"
220 "<b>TSR</b>=Top, slightly right|<b>TR</b>=Top right|<br>"
221 "<b>L</b>=Left|<b>R</b>=Right|<br>"
222 "<b>BL</b>=Bottom left|<b>BSL</b>=Bottom, slightly left|<b>B</b>=Bottom middle|<br>"
223 "<b>BSR</b>=Bottom, slightly right|<b>BR</b>=Bottom right]" ), origin )
227 + QStringLiteral(
"[<b>OL</b>=On line|<b>AL</b>=Above line|<b>BL</b>=Below line|<br>"
228 "<b>LO</b>=Respect line orientation]" ), origin )
237 "<b>Half</b>|<b>Cap</b>|<b>Top</b>]" ), origin )
262 : predefinedPositionOrder( *DEFAULT_PLACEMENT_ORDER() )
265 initPropertyDefinitions();
274 , mDataDefinedProperties( s.mDataDefinedProperties )
309 mPolygonPlacementFlags = s.mPolygonPlacementFlags;
325 mRotationUnit = s.mRotationUnit;
346 mDataDefinedProperties = s.mDataDefinedProperties;
348 mCallout.reset( s.mCallout ? s.mCallout->clone() :
nullptr );
350 mPlacementSettings = s.mPlacementSettings;
351 mLineSettings = s.mLineSettings;
352 mObstacleSettings = s.mObstacleSettings;
353 mThinningSettings = s.mThinningSettings;
360 mLegendString = s.mLegendString;
362 mUnplacedVisibility = s.mUnplacedVisibility;
412 for (
const QString &name : referencedColumns )
414 attributeNames.insert( name );
473 for (
const QString &name : referencedColumns )
475 attributeNames.insert( name );
482 const auto referencedColumns = mCallout->referencedFields( context );
483 for (
const QString &name : referencedColumns )
485 attributeNames.insert( name );
494 QSet<QString> referenced;
522 referenced.unite( mCallout->referencedFields( context ) );
530 if ( mRenderStarted )
532 qWarning(
"Start render called for when a previous render was already underway!!" );
559 mCallout->startRender( context );
562 mRenderStarted =
true;
567 if ( !mRenderStarted )
569 qWarning(
"Stop render called for QgsPalLayerSettings without a startRender call!" );
575 mCallout->stopRender( context );
578 mRenderStarted =
false;
588 if ( mRenderStarted )
590 qWarning(
"stopRender was not called on QgsPalLayerSettings object!" );
601 initPropertyDefinitions();
602 return *sPropertyDefinitions();
616 return mRotationUnit;
621 mRotationUnit = angleUnit;
627 QString newValue = value;
628 if ( !value.isEmpty() && !value.contains( QLatin1String(
"~~" ) ) )
631 values << QStringLiteral(
"1" );
632 values << QStringLiteral(
"0" );
635 newValue = values.join( QLatin1String(
"~~" ) );
643 QString newPropertyName =
"labeling/dataDefined/" + sPropertyDefinitions()->value( p ).name();
644 QVariant newPropertyField = layer->
customProperty( newPropertyName, QVariant() );
646 if ( !newPropertyField.isValid() )
649 QString ddString = newPropertyField.toString();
651 if ( !ddString.isEmpty() && ddString != QLatin1String(
"0~~0~~~~" ) )
655 QStringList ddv = newStyleString.split( QStringLiteral(
"~~" ) );
657 bool active = ddv.at( 0 ).toInt();
658 if ( ddv.at( 1 ).toInt() )
674void QgsPalLayerSettings::readOldDataDefinedPropertyMap(
QgsVectorLayer *layer, QDomElement *parentElem )
676 if ( !layer && !parentElem )
681 QgsPropertiesDefinition::const_iterator i = sPropertyDefinitions()->constBegin();
682 for ( ; i != sPropertyDefinitions()->constEnd(); ++i )
687 readOldDataDefinedProperty( layer,
static_cast< Property >( i.key() ) );
689 else if ( parentElem )
692 QDomElement e = parentElem->firstChildElement( i.value().name() );
695 bool active = e.attribute( QStringLiteral(
"active" ) ).compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
696 bool isExpression = e.attribute( QStringLiteral(
"useExpr" ) ).compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
710void QgsPalLayerSettings::readFromLayerCustomProperties(
QgsVectorLayer *layer )
712 if ( layer->
customProperty( QStringLiteral(
"labeling" ) ).toString() != QLatin1String(
"pal" ) )
714 if ( layer->
geometryType() == Qgis::GeometryType::Point )
719 if ( layer->
geometryType() == Qgis::GeometryType::Polygon )
737 QDomDocument doc( QStringLiteral(
"substitutions" ) );
738 doc.setContent( layer->
customProperty( QStringLiteral(
"labeling/substitutions" ) ).toString() );
739 QDomElement replacementElem = doc.firstChildElement( QStringLiteral(
"substitutions" ) );
760 mLineSettings.
setPlacementFlags(
static_cast< Qgis::LabelLinePlacementFlags
>( layer->
customProperty( QStringLiteral(
"labeling/placementFlags" ) ).toUInt() ) );
768 distUnits = layer->
customProperty( QStringLiteral(
"labeling/distInMapUnits" ) ).toBool() ? Qgis::RenderUnit::MapUnits : Qgis::RenderUnit::Millimeters;
769 if ( layer->
customProperty( QStringLiteral(
"labeling/distMapUnitScale" ) ).toString().isEmpty() )
772 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/distMapUnitMinScale" ), 0.0 ).toDouble();
774 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/distMapUnitMaxScale" ), 0.0 ).toDouble();
785 if ( layer->
customProperty( QStringLiteral(
"labeling/labelOffsetInMapUnits" ), QVariant(
true ) ).toBool() )
790 if ( layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() )
793 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitMinScale" ), 0.0 ).toDouble();
795 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
803 QVariant tempAngle = layer->
customProperty( QStringLiteral(
"labeling/angleOffset" ), QVariant() );
804 if ( tempAngle.isValid() )
806 double oldAngle = layer->
customProperty( QStringLiteral(
"labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble();
815 mRotationUnit = layer->
customEnumProperty( QStringLiteral(
"labeling/rotationUnit" ), Qgis::AngleUnit::Degrees );
820 switch ( layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceUnit" ), QVariant( 1 ) ).toUInt() )
835 if ( layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() )
838 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitMinScale" ), 0.0 ).toDouble();
840 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitMaxScale" ), 0.0 ).toDouble();
849 double scalemn = layer->
customProperty( QStringLiteral(
"labeling/scaleMin" ), QVariant( 0 ) ).toDouble();
850 double scalemx = layer->
customProperty( QStringLiteral(
"labeling/scaleMax" ), QVariant( 0 ) ).toDouble();
853 QVariant scalevis = layer->
customProperty( QStringLiteral(
"labeling/scaleVisibility" ), QVariant() );
854 if ( scalevis.isValid() )
860 else if ( scalemn > 0 || scalemx > 0 )
876 if ( layer->
customProperty( QStringLiteral(
"labeling/displayAll" ), QVariant(
false ) ).toBool() )
894 mObstacleSettings.
setFactor( layer->
customProperty( QStringLiteral(
"labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble() );
896 zIndex = layer->
customProperty( QStringLiteral(
"labeling/zIndex" ), QVariant( 0.0 ) ).toDouble();
898 mDataDefinedProperties.
clear();
899 if ( layer->
customProperty( QStringLiteral(
"labeling/ddProperties" ) ).isValid() )
901 QDomDocument doc( QStringLiteral(
"dd" ) );
902 doc.setContent( layer->
customProperty( QStringLiteral(
"labeling/ddProperties" ) ).toString() );
903 QDomElement elem = doc.firstChildElement( QStringLiteral(
"properties" ) );
904 mDataDefinedProperties.
readXml( elem, *sPropertyDefinitions() );
909 readOldDataDefinedPropertyMap( layer,
nullptr );
953 QDomElement textStyleElem = elem.firstChildElement( QStringLiteral(
"text-style" ) );
954 fieldName = textStyleElem.attribute( QStringLiteral(
"fieldName" ) );
955 isExpression = textStyleElem.attribute( QStringLiteral(
"isExpression" ) ).toInt();
957 mFormat.
readXml( elem, context );
959 previewBkgrdColor = QColor( textStyleElem.attribute( QStringLiteral(
"previewBkgrdColor" ), QStringLiteral(
"#ffffff" ) ) );
962 useSubstitutions = textStyleElem.attribute( QStringLiteral(
"useSubstitutions" ) ).toInt();
963 mLegendString = textStyleElem.attribute( QStringLiteral(
"legendString" ), QObject::tr(
"Aa" ) );
966 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral(
"text-format" ) );
967 wrapChar = textFormatElem.attribute( QStringLiteral(
"wrapChar" ) );
968 autoWrapLength = textFormatElem.attribute( QStringLiteral(
"autoWrapLength" ), QStringLiteral(
"0" ) ).toInt();
969 useMaxLineLengthForAutoWrap = textFormatElem.attribute( QStringLiteral(
"useMaxLineLengthForAutoWrap" ), QStringLiteral(
"1" ) ).toInt();
970 multilineAlign =
static_cast< Qgis::LabelMultiLineAlignment >( textFormatElem.attribute( QStringLiteral(
"multilineAlign" ), QString::number(
static_cast< int >( Qgis::LabelMultiLineAlignment::FollowPlacement ) ) ).toUInt() );
971 mLineSettings.
setAddDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"addDirectionSymbol" ) ).toInt() );
972 mLineSettings.
setLeftDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"leftDirectionSymbol" ), QStringLiteral(
"<" ) ) );
973 mLineSettings.
setRightDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"rightDirectionSymbol" ), QStringLiteral(
">" ) ) );
974 mLineSettings.
setReverseDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"reverseDirectionSymbol" ) ).toInt() );
976 formatNumbers = textFormatElem.attribute( QStringLiteral(
"formatNumbers" ) ).toInt();
977 decimals = textFormatElem.attribute( QStringLiteral(
"decimals" ) ).toInt();
978 plusSign = textFormatElem.attribute( QStringLiteral(
"plussign" ) ).toInt();
981 QDomElement placementElem = elem.firstChildElement( QStringLiteral(
"placement" ) );
983 mLineSettings.
setPlacementFlags(
static_cast< Qgis::LabelLinePlacementFlags
>( placementElem.attribute( QStringLiteral(
"placementFlags" ) ).toUInt() ) );
986 centroidWhole = placementElem.attribute( QStringLiteral(
"centroidWhole" ), QStringLiteral(
"0" ) ).toInt();
987 centroidInside = placementElem.attribute( QStringLiteral(
"centroidInside" ), QStringLiteral(
"0" ) ).toInt();
991 fitInPolygonOnly = placementElem.attribute( QStringLiteral(
"fitInPolygonOnly" ), QStringLiteral(
"0" ) ).toInt();
992 dist = placementElem.attribute( QStringLiteral(
"dist" ) ).toDouble();
993 if ( !placementElem.hasAttribute( QStringLiteral(
"distUnits" ) ) )
995 if ( placementElem.attribute( QStringLiteral(
"distInMapUnits" ) ).toInt() )
998 distUnits = Qgis::RenderUnit::Millimeters;
1004 if ( !placementElem.hasAttribute( QStringLiteral(
"distMapUnitScale" ) ) )
1007 double oldMin = placementElem.attribute( QStringLiteral(
"distMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1009 double oldMax = placementElem.attribute( QStringLiteral(
"distMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1017 quadOffset =
static_cast< Qgis::LabelQuadrantPosition >( placementElem.attribute( QStringLiteral(
"quadOffset" ), QString::number(
static_cast< int >( Qgis::LabelQuadrantPosition::Over ) ) ).toUInt() );
1018 xOffset = placementElem.attribute( QStringLiteral(
"xOffset" ), QStringLiteral(
"0" ) ).toDouble();
1019 yOffset = placementElem.attribute( QStringLiteral(
"yOffset" ), QStringLiteral(
"0" ) ).toDouble();
1020 if ( !placementElem.hasAttribute( QStringLiteral(
"offsetUnits" ) ) )
1022 offsetUnits = placementElem.attribute( QStringLiteral(
"labelOffsetInMapUnits" ), QStringLiteral(
"1" ) ).toInt() ? Qgis::RenderUnit::MapUnits : Qgis::RenderUnit::Millimeters;
1028 if ( !placementElem.hasAttribute( QStringLiteral(
"labelOffsetMapUnitScale" ) ) )
1031 double oldMin = placementElem.attribute( QStringLiteral(
"labelOffsetMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1033 double oldMax = placementElem.attribute( QStringLiteral(
"labelOffsetMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1041 if ( placementElem.hasAttribute( QStringLiteral(
"angleOffset" ) ) )
1043 double oldAngle = placementElem.attribute( QStringLiteral(
"angleOffset" ), QStringLiteral(
"0" ) ).toDouble();
1048 angleOffset = placementElem.attribute( QStringLiteral(
"rotationAngle" ), QStringLiteral(
"0" ) ).toDouble();
1051 preserveRotation = placementElem.attribute( QStringLiteral(
"preserveRotation" ), QStringLiteral(
"1" ) ).toInt();
1053 QString rotationUnitString = placementElem.attribute( QStringLiteral(
"rotationUnit" ),
qgsEnumValueToKey( Qgis::AngleUnit::Degrees ) );
1054 if ( rotationUnitString.startsWith( QLatin1String(
"Angle" ) ) )
1057 rotationUnitString = rotationUnitString.mid( 5 );
1060 mRotationUnit =
qgsEnumKeyToValue( rotationUnitString, Qgis::AngleUnit::Degrees );
1062 maxCurvedCharAngleIn = placementElem.attribute( QStringLiteral(
"maxCurvedCharAngleIn" ), QStringLiteral(
"25" ) ).toDouble();
1063 maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral(
"maxCurvedCharAngleOut" ), QStringLiteral(
"-25" ) ).toDouble();
1064 priority = placementElem.attribute( QStringLiteral(
"priority" ) ).toInt();
1065 repeatDistance = placementElem.attribute( QStringLiteral(
"repeatDistance" ), QStringLiteral(
"0" ) ).toDouble();
1066 if ( !placementElem.hasAttribute( QStringLiteral(
"repeatDistanceUnits" ) ) )
1069 switch ( placementElem.attribute( QStringLiteral(
"repeatDistanceUnit" ), QString::number( 1 ) ).toUInt() )
1089 if ( !placementElem.hasAttribute( QStringLiteral(
"repeatDistanceMapUnitScale" ) ) )
1092 double oldMin = placementElem.attribute( QStringLiteral(
"repeatDistanceMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1094 double oldMax = placementElem.attribute( QStringLiteral(
"repeatDistanceMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1102 mLineSettings.
setOverrunDistance( placementElem.attribute( QStringLiteral(
"overrunDistance" ), QStringLiteral(
"0" ) ).toDouble() );
1105 mLineSettings.
setLineAnchorPercent( placementElem.attribute( QStringLiteral(
"lineAnchorPercent" ), QStringLiteral(
"0.5" ) ).toDouble() );
1111 geometryGenerator = placementElem.attribute( QStringLiteral(
"geometryGenerator" ) );
1114 QString geometryTypeKey = placementElem.attribute( QStringLiteral(
"geometryGeneratorType" ) );
1116 if ( geometryTypeKey.endsWith( QLatin1String(
"Geometry" ) ) )
1117 geometryTypeKey.chop( 8 );
1122 QString layerTypeKey = placementElem.attribute( QStringLiteral(
"layerType" ) );
1124 if ( layerTypeKey.endsWith( QLatin1String(
"Geometry" ) ) )
1125 layerTypeKey.chop( 8 );
1130 mPlacementSettings.
setAllowDegradedPlacement( placementElem.attribute( QStringLiteral(
"allowDegraded" ), QStringLiteral(
"0" ) ).toInt() );
1133 QDomElement renderingElem = elem.firstChildElement( QStringLiteral(
"rendering" ) );
1135 drawLabels = renderingElem.attribute( QStringLiteral(
"drawLabels" ), QStringLiteral(
"1" ) ).toInt();
1137 maximumScale = renderingElem.attribute( QStringLiteral(
"scaleMin" ), QStringLiteral(
"0" ) ).toDouble();
1138 minimumScale = renderingElem.attribute( QStringLiteral(
"scaleMax" ), QStringLiteral(
"0" ) ).toDouble();
1139 scaleVisibility = renderingElem.attribute( QStringLiteral(
"scaleVisibility" ) ).toInt();
1141 fontLimitPixelSize = renderingElem.attribute( QStringLiteral(
"fontLimitPixelSize" ), QStringLiteral(
"0" ) ).toInt();
1142 fontMinPixelSize = renderingElem.attribute( QStringLiteral(
"fontMinPixelSize" ), QStringLiteral(
"0" ) ).toInt();
1143 fontMaxPixelSize = renderingElem.attribute( QStringLiteral(
"fontMaxPixelSize" ), QStringLiteral(
"10000" ) ).toInt();
1145 if ( placementElem.hasAttribute( QStringLiteral(
"overlapHandling" ) ) )
1152 if ( renderingElem.attribute( QStringLiteral(
"displayAll" ), QStringLiteral(
"0" ) ).toInt() )
1163 upsidedownLabels =
static_cast< Qgis::UpsideDownLabelHandling >( renderingElem.attribute( QStringLiteral(
"upsidedownLabels" ), QString::number(
static_cast< int >( Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels ) ) ).toUInt() );
1165 labelPerPart = renderingElem.attribute( QStringLiteral(
"labelPerPart" ) ).toInt();
1166 mLineSettings.
setMergeLines( renderingElem.attribute( QStringLiteral(
"mergeLines" ) ).toInt() );
1167 mThinningSettings.
setMinimumFeatureSize( renderingElem.attribute( QStringLiteral(
"minFeatureSize" ) ).toDouble() );
1168 mThinningSettings.
setLimitNumberLabelsEnabled( renderingElem.attribute( QStringLiteral(
"limitNumLabels" ), QStringLiteral(
"0" ) ).toInt() );
1169 mThinningSettings.
setMaximumNumberLabels( renderingElem.attribute( QStringLiteral(
"maxNumLabels" ), QStringLiteral(
"2000" ) ).toInt() );
1170 mObstacleSettings.
setIsObstacle( renderingElem.attribute( QStringLiteral(
"obstacle" ), QStringLiteral(
"1" ) ).toInt() );
1171 mObstacleSettings.
setFactor( renderingElem.attribute( QStringLiteral(
"obstacleFactor" ), QStringLiteral(
"1" ) ).toDouble() );
1173 zIndex = renderingElem.attribute( QStringLiteral(
"zIndex" ), QStringLiteral(
"0.0" ) ).toDouble();
1176 QDomElement ddElem = elem.firstChildElement( QStringLiteral(
"dd_properties" ) );
1177 if ( !ddElem.isNull() )
1179 mDataDefinedProperties.
readXml( ddElem, *sPropertyDefinitions() );
1184 mDataDefinedProperties.
clear();
1185 QDomElement ddElem = elem.firstChildElement( QStringLiteral(
"data-defined" ) );
1186 readOldDataDefinedPropertyMap(
nullptr, &ddElem );
1227 const QString calloutType = elem.attribute( QStringLiteral(
"calloutType" ) );
1228 if ( calloutType.isEmpty() )
1240 QDomElement textStyleElem = mFormat.
writeXml( doc, context );
1243 textStyleElem.setAttribute( QStringLiteral(
"fieldName" ),
fieldName );
1244 textStyleElem.setAttribute( QStringLiteral(
"isExpression" ),
isExpression );
1245 QDomElement replacementElem = doc.createElement( QStringLiteral(
"substitutions" ) );
1247 textStyleElem.appendChild( replacementElem );
1248 textStyleElem.setAttribute( QStringLiteral(
"useSubstitutions" ),
useSubstitutions );
1249 textStyleElem.setAttribute( QStringLiteral(
"legendString" ), mLegendString );
1252 QDomElement textFormatElem = doc.createElement( QStringLiteral(
"text-format" ) );
1253 textFormatElem.setAttribute( QStringLiteral(
"wrapChar" ),
wrapChar );
1254 textFormatElem.setAttribute( QStringLiteral(
"autoWrapLength" ),
autoWrapLength );
1256 textFormatElem.setAttribute( QStringLiteral(
"multilineAlign" ),
static_cast< unsigned int >(
multilineAlign ) );
1257 textFormatElem.setAttribute( QStringLiteral(
"addDirectionSymbol" ), mLineSettings.
addDirectionSymbol() );
1258 textFormatElem.setAttribute( QStringLiteral(
"leftDirectionSymbol" ), mLineSettings.
leftDirectionSymbol() );
1259 textFormatElem.setAttribute( QStringLiteral(
"rightDirectionSymbol" ), mLineSettings.
rightDirectionSymbol() );
1260 textFormatElem.setAttribute( QStringLiteral(
"reverseDirectionSymbol" ), mLineSettings.
reverseDirectionSymbol() );
1261 textFormatElem.setAttribute( QStringLiteral(
"placeDirectionSymbol" ),
static_cast< unsigned int >( mLineSettings.
directionSymbolPlacement() ) );
1262 textFormatElem.setAttribute( QStringLiteral(
"formatNumbers" ),
formatNumbers );
1263 textFormatElem.setAttribute( QStringLiteral(
"decimals" ),
decimals );
1264 textFormatElem.setAttribute( QStringLiteral(
"plussign" ),
plusSign );
1267 QDomElement placementElem = doc.createElement( QStringLiteral(
"placement" ) );
1268 placementElem.setAttribute( QStringLiteral(
"placement" ),
static_cast< int >(
placement ) );
1269 placementElem.setAttribute( QStringLiteral(
"polygonPlacementFlags" ),
static_cast< int >( mPolygonPlacementFlags ) );
1270 placementElem.setAttribute( QStringLiteral(
"placementFlags" ),
static_cast< unsigned int >( mLineSettings.
placementFlags() ) );
1271 placementElem.setAttribute( QStringLiteral(
"centroidWhole" ),
centroidWhole );
1272 placementElem.setAttribute( QStringLiteral(
"centroidInside" ),
centroidInside );
1274 placementElem.setAttribute( QStringLiteral(
"fitInPolygonOnly" ),
fitInPolygonOnly );
1275 placementElem.setAttribute( QStringLiteral(
"dist" ),
dist );
1278 placementElem.setAttribute( QStringLiteral(
"offsetType" ),
static_cast< unsigned int >(
offsetType ) );
1279 placementElem.setAttribute( QStringLiteral(
"quadOffset" ),
static_cast< unsigned int >(
quadOffset ) );
1280 placementElem.setAttribute( QStringLiteral(
"xOffset" ),
xOffset );
1281 placementElem.setAttribute( QStringLiteral(
"yOffset" ),
yOffset );
1284 placementElem.setAttribute( QStringLiteral(
"rotationAngle" ),
angleOffset );
1285 placementElem.setAttribute( QStringLiteral(
"preserveRotation" ),
preserveRotation );
1288 const QString rotationUnitString = QStringLiteral(
"Angle" ) +
qgsEnumValueToKey( mRotationUnit );
1289 placementElem.setAttribute( QStringLiteral(
"rotationUnit" ), rotationUnitString );
1293 placementElem.setAttribute( QStringLiteral(
"priority" ),
priority );
1294 placementElem.setAttribute( QStringLiteral(
"repeatDistance" ),
repeatDistance );
1297 placementElem.setAttribute( QStringLiteral(
"overrunDistance" ), mLineSettings.
overrunDistance() );
1300 placementElem.setAttribute( QStringLiteral(
"lineAnchorPercent" ), mLineSettings.
lineAnchorPercent() );
1301 placementElem.setAttribute( QStringLiteral(
"lineAnchorType" ),
static_cast< int >( mLineSettings.
anchorType() ) );
1302 placementElem.setAttribute( QStringLiteral(
"lineAnchorClipping" ),
static_cast< int >( mLineSettings.
anchorClipping() ) );
1305 placementElem.setAttribute( QStringLiteral(
"geometryGenerator" ),
geometryGenerator );
1312 placementElem.setAttribute( QStringLiteral(
"allowDegraded" ), mPlacementSettings.
allowDegradedPlacement() ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
1315 QDomElement renderingElem = doc.createElement( QStringLiteral(
"rendering" ) );
1316 renderingElem.setAttribute( QStringLiteral(
"drawLabels" ),
drawLabels );
1317 renderingElem.setAttribute( QStringLiteral(
"scaleVisibility" ),
scaleVisibility );
1318 renderingElem.setAttribute( QStringLiteral(
"scaleMin" ),
maximumScale );
1319 renderingElem.setAttribute( QStringLiteral(
"scaleMax" ),
minimumScale );
1320 renderingElem.setAttribute( QStringLiteral(
"fontLimitPixelSize" ),
fontLimitPixelSize );
1321 renderingElem.setAttribute( QStringLiteral(
"fontMinPixelSize" ),
fontMinPixelSize );
1322 renderingElem.setAttribute( QStringLiteral(
"fontMaxPixelSize" ),
fontMaxPixelSize );
1323 renderingElem.setAttribute( QStringLiteral(
"upsidedownLabels" ),
static_cast< unsigned int >(
upsidedownLabels ) );
1325 renderingElem.setAttribute( QStringLiteral(
"labelPerPart" ),
labelPerPart );
1326 renderingElem.setAttribute( QStringLiteral(
"mergeLines" ), mLineSettings.
mergeLines() );
1327 renderingElem.setAttribute( QStringLiteral(
"minFeatureSize" ), mThinningSettings.
minimumFeatureSize() );
1329 renderingElem.setAttribute( QStringLiteral(
"maxNumLabels" ), mThinningSettings.
maximumNumberLabels() );
1330 renderingElem.setAttribute( QStringLiteral(
"obstacle" ), mObstacleSettings.
isObstacle() );
1331 renderingElem.setAttribute( QStringLiteral(
"obstacleFactor" ), mObstacleSettings.
factor() );
1332 renderingElem.setAttribute( QStringLiteral(
"obstacleType" ),
static_cast< unsigned int >( mObstacleSettings.
type() ) );
1333 renderingElem.setAttribute( QStringLiteral(
"zIndex" ),
zIndex );
1334 renderingElem.setAttribute( QStringLiteral(
"unplacedVisibility" ),
static_cast< int >( mUnplacedVisibility ) );
1336 QDomElement ddElem = doc.createElement( QStringLiteral(
"dd_properties" ) );
1337 mDataDefinedProperties.
writeXml( ddElem, *sPropertyDefinitions() );
1339 QDomElement elem = doc.createElement( QStringLiteral(
"settings" ) );
1340 elem.appendChild( textStyleElem );
1341 elem.appendChild( textFormatElem );
1342 elem.appendChild( placementElem );
1343 elem.appendChild( renderingElem );
1344 elem.appendChild( ddElem );
1348 elem.setAttribute( QStringLiteral(
"calloutType" ), mCallout->type() );
1349 mCallout->saveProperties( doc, elem, context );
1364 QPixmap pixmap( size );
1365 pixmap.fill( Qt::transparent );
1367 painter.begin( &pixmap );
1369 painter.setRenderHint( QPainter::Antialiasing );
1371 QRect rect( 0, 0, size.width(), size.height() );
1374 painter.setPen( Qt::NoPen );
1376 if ( ( background1.lightnessF() < 0.7 ) )
1378 background1 = background1.darker( 125 );
1382 background1 = background1.lighter( 125 );
1385 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1386 linearGrad.setColorAt( 0, background1 );
1387 linearGrad.setColorAt( 1, background2 );
1388 painter.setBrush( QBrush( linearGrad ) );
1389 if ( size.width() > 30 )
1391 painter.drawRoundedRect( rect, 6, 6 );
1396 painter.drawRect( rect );
1398 painter.setBrush( Qt::NoBrush );
1399 painter.setPen( Qt::NoPen );
1408 QWidget *activeWindow = QApplication::activeWindow();
1409 const double logicalDpiX = activeWindow && activeWindow->screen() ? activeWindow->screen()->logicalDotsPerInchX() : 96.0;
1418 xtrans = tempFormat.
buffer().
sizeUnit() == Qgis::RenderUnit::Percentage
1419 ? fontSize * tempFormat.
buffer().
size() / 100
1424 double ytrans = 0.0;
1426 ytrans = std::max( ytrans, tempFormat.
buffer().
sizeUnit() == Qgis::RenderUnit::Percentage
1427 ? fontSize * tempFormat.
buffer().
size() / 100
1432 const QStringList text = QStringList() << ( previewText.isEmpty() ? settings.
legendString() : previewText );
1434 QRectF textRect = rect;
1435 textRect.setLeft( xtrans + padding );
1436 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1438 if ( textRect.width() > 2000 )
1439 textRect.setWidth( 2000 - 2 * padding );
1441 const double bottom = textRect.height() / 2 + textHeight / 2;
1442 textRect.setTop( bottom - textHeight );
1443 textRect.setBottom( bottom );
1454 QRectF labelRect( textRect.left() + ( textRect.width() - textWidth ) / 2.0, textRect.top(), textWidth, textRect.height() );
1461 if ( size.width() > 30 )
1466 rect.width() - iconWidth * 3, rect.height() - iconWidth * 3,
1467 iconWidth * 2, iconWidth * 2 ), Qt::AlignRight | Qt::AlignBottom );
1471 painter.setBrush( Qt::NoBrush );
1473 if ( size.width() > 30 )
1475 painter.drawRoundedRect( rect, 6, 6 );
1480 painter.drawRect( rect );
1489 return mUnplacedVisibility;
1494 mUnplacedVisibility = visibility;
1499 return QgsPalLabeling::checkMinimumSizeMM(
ct, geom, minSize );
1502void QgsPalLayerSettings::calculateLabelSize(
const QFontMetricsF *fm,
const QString &text,
double &labelX,
double &labelY,
const QgsFeature *f,
QgsRenderContext *context,
double *rotatedLabelX,
double *rotatedLabelY,
QgsTextDocument *document,
QgsTextDocumentMetrics *documentMetrics, QRectF *outerBounds )
1509 QString textCopy( text );
1512 std::unique_ptr< QgsRenderContext > scopedRc;
1517 scopedRc->expressionContext().setFeature( *f );
1633 if ( wrapchr.isEmpty() )
1635 wrapchr = QStringLiteral(
"\n" );
1640 && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1642 QString dirSym = leftDirSymb;
1644 if ( fm->horizontalAdvance( rightDirSymb ) > fm->horizontalAdvance( dirSym ) )
1645 dirSym = rightDirSymb;
1647 switch ( placeDirSymb )
1650 textCopy.append( dirSym );
1655 textCopy.prepend( dirSym + QStringLiteral(
"\n" ) );
1660 double w = 0.0, h = 0.0, rw = 0.0, rh = 0.0;
1661 double labelHeight = fm->ascent() + fm->descent();
1668 const QSizeF size = documentMetrics->
documentSize( Qgis::TextLayoutMode::Labeling, orientation );
1675 const int lines = multiLineSplit.size();
1679 switch ( orientation )
1681 case Qgis::TextOrientation::Horizontal:
1683 h += fm->height() +
static_cast< double >( ( lines - 1 ) * ( mFormat.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( labelHeight * multilineH ) : lineHeightPainterUnits ) );
1685 for (
const QString &line : std::as_const( multiLineSplit ) )
1687 w = std::max( w, fm->horizontalAdvance( line ) );
1692 case Qgis::TextOrientation::Vertical:
1694 double letterSpacing = mFormat.
scaledFont( *context ).letterSpacing();
1695 double labelWidth = fm->maxWidth();
1696 w = labelWidth + ( lines - 1 ) * ( mFormat.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( labelWidth * multilineH ) : lineHeightPainterUnits );
1698 int maxLineLength = 0;
1699 for (
const QString &line : std::as_const( multiLineSplit ) )
1701 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1703 h = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1707 case Qgis::TextOrientation::RotationBased:
1709 double widthHorizontal = 0.0;
1710 for (
const QString &line : std::as_const( multiLineSplit ) )
1712 widthHorizontal = std::max( w, fm->horizontalAdvance( line ) );
1715 double widthVertical = 0.0;
1716 double letterSpacing = mFormat.
scaledFont( *context ).letterSpacing();
1717 double labelWidth = fm->maxWidth();
1718 widthVertical = labelWidth + ( lines - 1 ) * ( mFormat.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( labelWidth * multilineH ) : lineHeightPainterUnits );
1720 double heightHorizontal = 0.0;
1721 heightHorizontal += fm->height() +
static_cast< double >( ( lines - 1 ) * ( mFormat.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( labelHeight * multilineH ) : lineHeightPainterUnits ) );
1723 double heightVertical = 0.0;
1724 int maxLineLength = 0;
1725 for (
const QString &line : std::as_const( multiLineSplit ) )
1727 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1729 heightVertical = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1731 w = widthHorizontal;
1732 rw = heightVertical;
1733 h = heightHorizontal;
1742 labelX = std::fabs( ptSize.
x() -
ptZero.
x() );
1743 labelY = std::fabs( ptSize.
y() -
ptZero.
y() );
1748 if ( rotatedLabelX && rotatedLabelY )
1750 *rotatedLabelX = rw * uPP;
1751 *rotatedLabelY = rh * uPP;
1755 if ( outerBounds && documentMetrics )
1757 const QRectF outerBoundsPixels = documentMetrics->
outerBounds( Qgis::TextLayoutMode::Labeling, orientation );
1759 *outerBounds = QRectF( outerBoundsPixels.left() * uPP,
1760 outerBoundsPixels.top() * uPP,
1761 outerBoundsPixels.width() * uPP,
1762 outerBoundsPixels.height() * uPP );
1777 bool isObstacle = mObstacleSettings.
isObstacle();
1785 return registerObstacleFeature( f, context, obstacleGeometry );
1800 if ( obstacleGeometry.
isNull() )
1813 dataDefinedValues.clear();
1830 if ( useScaleVisibility )
1843 maxScale = 1 / std::fabs( maxScale );
1862 minScale = 1 / std::fabs( minScale );
1871 QFont labelFont = mFormat.
font();
1879 QString units = exprVal.toString();
1880 if ( !units.isEmpty() )
1890 double fontSize = mFormat.
size();
1896 if ( fontSize <= 0.0 )
1903 if ( fontPixelSize < 1 )
1907 labelFont.setPixelSize( fontPixelSize );
1912 if ( fontunits == Qgis::RenderUnit::MapUnits )
1919 if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1931 labelFont.setCapitalization( QFont::MixedCase );
1933 parseTextStyle( labelFont, fontunits, context );
1936 parseTextFormatting( context );
1937 parseTextBuffer( context );
1938 parseTextMask( context );
1939 parseShapeBackground( context );
1940 parseDropShadow( context );
1988 QString fcase = exprVal.toString().trimmed();
1989 QgsDebugMsgLevel( QStringLiteral(
"exprVal FontCase:%1" ).arg( fcase ), 4 );
1991 if ( !fcase.isEmpty() )
1993 if ( fcase.compare( QLatin1String(
"NoChange" ), Qt::CaseInsensitive ) == 0 )
1997 else if ( fcase.compare( QLatin1String(
"Upper" ), Qt::CaseInsensitive ) == 0 )
2001 else if ( fcase.compare( QLatin1String(
"Lower" ), Qt::CaseInsensitive ) == 0 )
2005 else if ( fcase.compare( QLatin1String(
"Capitalize" ), Qt::CaseInsensitive ) == 0 )
2009 else if ( fcase.compare( QLatin1String(
"Title" ), Qt::CaseInsensitive ) == 0 )
2013#if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
2014 else if ( fcase.compare( QLatin1String(
"SmallCaps" ), Qt::CaseInsensitive ) == 0 )
2018 else if ( fcase.compare( QLatin1String(
"AllSmallCaps" ), Qt::CaseInsensitive ) == 0 )
2034 if ( evalFormatNumbers )
2038 if ( decimalPlaces <= 0 )
2044 QVariant textV( labelText );
2046 double d = textV.toDouble( &ok );
2049 QString numberFormat;
2050 if ( d > 0 && signPlus )
2052 numberFormat.append(
'+' );
2054 numberFormat.append(
"%1" );
2055 labelText = numberFormat.arg( d, 0,
'f', decimalPlaces );
2060 std::unique_ptr<QFontMetricsF> labelFontMetrics(
new QFontMetricsF( labelFont ) );
2063 double rotatedLabelX;
2064 double rotatedLabelY;
2069 if (
format().allowHtmlFormatting() && !labelText.isEmpty() )
2073 calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight,
mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, &doc, &documentMetrics, &outerBounds );
2077 calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight,
mCurFeat, &context, &rotatedLabelX, &rotatedLabelY,
nullptr,
nullptr, &outerBounds );
2082 double maxcharanglein = 20.0;
2083 double maxcharangleout = -20.0;
2101 maxcharanglein = std::clamp(
static_cast< double >( maxcharanglePt.x() ), 20.0, 60.0 );
2102 maxcharangleout = std::clamp(
static_cast< double >( maxcharanglePt.y() ), 20.0, 95.0 );
2106 maxcharangleout = -( std::fabs( maxcharangleout ) );
2127 QString
str = exprVal.toString().trimmed();
2130 if ( !
str.isEmpty() )
2132 if (
str.compare( QLatin1String(
"Visible" ), Qt::CaseInsensitive ) == 0 )
2134 wholeCentroid =
false;
2136 else if (
str.compare( QLatin1String(
"Whole" ), Qt::CaseInsensitive ) == 0 )
2138 wholeCentroid =
true;
2152 std::unique_ptr<QgsGeometry> scopedClonedGeom;
2158 geom = simplifier.
simplify( geom );
2171 && geom.
type() == Qgis::GeometryType::Polygon );
2175 bool doClip =
false;
2176 if ( !centroidPoly || !wholeCentroid )
2182 Qgis::LabelPolygonPlacementFlags polygonPlacement = mPolygonPlacementFlags;
2188 if ( dataDefinedOutside.type() == QVariant::String )
2190 const QString value = dataDefinedOutside.toString().trimmed();
2191 if ( value.compare( QLatin1String(
"force" ), Qt::CaseInsensitive ) == 0 )
2197 else if ( value.compare( QLatin1String(
"yes" ), Qt::CaseInsensitive ) == 0 )
2202 else if ( value.compare( QLatin1String(
"no" ), Qt::CaseInsensitive ) == 0 )
2210 if ( dataDefinedOutside.toBool() )
2227 if ( geom.
type() == Qgis::GeometryType::Line )
2246 permissibleZone = geom;
2255 if ( ( geom.
type() == Qgis::GeometryType::Polygon )
2282 double minimumSize = 0.0;
2286 if ( geom.
type() == Qgis::GeometryType::Line && mLineSettings.
mergeLines() )
2292 if ( !checkMinimumSizeMM( context, geom, featureThinningSettings.
minimumFeatureSize() ) )
2297 if ( !geos_geom_clone )
2326 bool layerDefinedRotation =
false;
2327 bool dataDefinedRotation =
false;
2328 double xPos = 0.0, yPos = 0.0,
angle = 0.0;
2329 double quadOffsetX = 0.0, quadOffsetY = 0.0;
2330 double offsetX = 0.0, offsetY = 0.0;
2342 bool ddFixedQuad =
false;
2351 int quadInt = exprVal.toInt( &ok );
2352 if ( ok && 0 <= quadInt && quadInt <= 8 )
2363 case Qgis::LabelQuadrantPosition::AboveLeft:
2367 case Qgis::LabelQuadrantPosition::Above:
2371 case Qgis::LabelQuadrantPosition::AboveRight:
2375 case Qgis::LabelQuadrantPosition::Left:
2379 case Qgis::LabelQuadrantPosition::Right:
2383 case Qgis::LabelQuadrantPosition::BelowLeft:
2387 case Qgis::LabelQuadrantPosition::Below:
2391 case Qgis::LabelQuadrantPosition::BelowRight:
2395 case Qgis::LabelQuadrantPosition::Over:
2422 QString units = exprVal.toString().trimmed();
2423 if ( !units.isEmpty() )
2429 offUnit = decodedUnits;
2444 layerDefinedRotation =
true;
2457 const double rotation = exprVal.toDouble( &ok );
2460 dataDefinedRotation =
true;
2463 Qgis::AngleUnit::Degrees );
2468 angle = ( 360 - rotationDegrees ) * M_PI / 180.0;
2473 bool hasDataDefinedPosition =
false;
2475 bool ddPosition =
false;
2487 bool ddXPos =
false, ddYPos =
false;
2488 xPos = xPosProperty.toDouble( &ddXPos );
2489 yPos = yPosProperty.toDouble( &ddYPos );
2490 if ( ddXPos && ddYPos )
2491 hasDataDefinedPosition =
true;
2502 if ( pointPosProperty.userType() == QMetaType::type(
"QgsReferencedGeometry" ) )
2507 if ( !referencedGeometryPoint.
isNull()
2511 else if ( pointPosProperty.userType() == QMetaType::type(
"QgsGeometry" ) )
2518 hasDataDefinedPosition =
true;
2529 if ( hasDataDefinedPosition )
2532 if ( layerDefinedRotation && !dataDefinedRotation )
2543 QString haliString = exprVal.toString();
2544 if ( haliString.compare( QLatin1String(
"Center" ), Qt::CaseInsensitive ) == 0 )
2546 xdiff -= labelWidth / 2.0;
2548 else if ( haliString.compare( QLatin1String(
"Right" ), Qt::CaseInsensitive ) == 0 )
2550 xdiff -= labelWidth;
2561 QString valiString = exprVal.toString();
2562 if ( valiString.compare( QLatin1String(
"Bottom" ), Qt::CaseInsensitive ) != 0 )
2564 if ( valiString.compare( QLatin1String(
"Top" ), Qt::CaseInsensitive ) == 0 )
2566 ydiff -= labelHeight;;
2570 double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2571 if ( valiString.compare( QLatin1String(
"Base" ), Qt::CaseInsensitive ) == 0 )
2573 ydiff -= labelHeight * descentRatio;
2577 double capHeightRatio = ( labelFontMetrics->boundingRect(
'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2578 ydiff -= labelHeight * capHeightRatio;
2579 if ( valiString.compare( QLatin1String(
"Half" ), Qt::CaseInsensitive ) == 0 )
2581 ydiff += labelHeight * ( capHeightRatio - descentRatio ) / 2.0;
2589 if ( dataDefinedRotation )
2592 double xd = xdiff * std::cos(
angle ) - ydiff * std::sin(
angle );
2593 double yd = xdiff * std::sin(
angle ) + ydiff * std::cos(
angle );
2603 if (
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( ddPoint.
constGet() ) )
2611 QgsMessageLog::logMessage( QObject::tr(
"Invalid data defined label position (%1, %2)" ).arg( xPos ).arg( yPos ), QObject::tr(
"Labeling" ) );
2612 hasDataDefinedPosition =
false;
2631 bool alwaysShow =
false;
2653 QString units = exprVal.toString().trimmed();
2654 if ( !units.isEmpty() )
2660 repeatUnits = decodedUnits;
2668 if ( repeatUnits != Qgis::RenderUnit::MapUnits )
2684 const double overrunSmoothDist = context.
convertToMapUnits( 1, Qgis::RenderUnit::Millimeters );
2686 bool labelAll =
labelPerPart && !hasDataDefinedPosition;
2687 if ( !hasDataDefinedPosition )
2697 std::unique_ptr< QgsTextLabelFeature > labelFeature = std::make_unique< QgsTextLabelFeature>( feature.
id(), std::move( geos_geom_clone ), QSizeF( labelWidth, labelHeight ) );
2698 labelFeature->setAnchorPosition( anchorPosition );
2699 labelFeature->setFeature( feature );
2700 labelFeature->setSymbol( symbol );
2701 labelFeature->setDocument( doc, documentMetrics );
2703 labelFeature->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
2706 labelFeature->setHasFixedPosition( hasDataDefinedPosition );
2707 labelFeature->setFixedPosition(
QgsPointXY( xPos, yPos ) );
2709 labelFeature->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !
qgsDoubleNear(
angle, 0.0 ) ) );
2710 labelFeature->setFixedAngle(
angle );
2711 labelFeature->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2712 labelFeature->setPositionOffset(
QgsPointXY( offsetX, offsetY ) );
2714 labelFeature->setAlwaysShow( alwaysShow );
2715 labelFeature->setRepeatDistance( repeatDist );
2716 labelFeature->setLabelText( labelText );
2717 labelFeature->setPermissibleZone( permissibleZone );
2718 labelFeature->setOverrunDistance( overrunDistanceEval );
2719 labelFeature->setOverrunSmoothDistance( overrunSmoothDist );
2723 labelFeature->setLabelAllParts( labelAll );
2725 labelFeature->setMinimumSize( minimumSize );
2726 if ( geom.
type() == Qgis::GeometryType::Point && !obstacleGeometry.
isNull() )
2729 labelFeature->setSymbolSize( QSizeF( obstacleGeometry.
boundingBox().
width(),
2733 if ( outerBounds.left() != 0 || outerBounds.top() != 0 || !
qgsDoubleNear( outerBounds.width(), labelWidth ) || !
qgsDoubleNear( outerBounds.height(), labelHeight ) )
2735 labelFeature->setOuterBounds( outerBounds );
2740 double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 );
2741 double bottomMargin = 1.0 + labelFontMetrics->descent();
2742 QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
2744 labelFeature->setVisualMargin( vm );
2747 QgsDebugMsgLevel( QStringLiteral(
"PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2748 labelFeature->setDefinedFont( labelFont );
2750 labelFeature->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 );
2751 labelFeature->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 );
2766 labelFeature->setTextMetrics(
QgsTextLabelFeature::calculateTextMetrics(
xform, context, labelFont, *labelFontMetrics, labelFont.letterSpacing(), labelFont.wordSpacing(), labelText,
format().allowHtmlFormatting() ? &doc :
nullptr,
format().allowHtmlFormatting() ? &documentMetrics :
nullptr ) );
2775 double distance =
dist;
2789 QString units = exprVal.toString().trimmed();
2790 QgsDebugMsgLevel( QStringLiteral(
"exprVal DistanceUnits:%1" ).arg( units ), 4 );
2791 if ( !units.isEmpty() )
2797 distUnit = decodedUnits;
2811 distance = ( distance < 0 ? -1 : 1 ) * std::max( std::fabs( distance ), 1.0 );
2823 distance = std::max( distance, 2.0 );
2828 distance = std::max( distance, 2.0 );
2835 labelFeature->setDistLabel( d );
2840 labelFeature->setHasFixedQuadrant(
true );
2845 labelFeature->setPolygonPlacementFlags( polygonPlacement );
2854 labelFeature->setZIndex( z );
2864 double priorityD = exprVal.toDouble( &ok );
2867 priorityD = std::clamp( priorityD, 0.0, 10.0 );
2868 priorityD = 1 - priorityD / 10.0;
2869 labelFeature->setPriority( priorityD );
2882 labelFeature->setAllowDegradedPlacement( allowDegradedPlacement );
2891 const QString cleanedString = handlingString.trimmed();
2892 if ( cleanedString.compare( QLatin1String(
"prevent" ), Qt::CaseInsensitive ) == 0 )
2894 else if ( cleanedString.compare( QLatin1String(
"allowifneeded" ), Qt::CaseInsensitive ) == 0 )
2896 else if ( cleanedString.compare( QLatin1String(
"alwaysallow" ), Qt::CaseInsensitive ) == 0 )
2899 labelFeature->setOverlapHandling( overlapHandling );
2906 labelFeature->setObstacleSettings( os );
2909 if ( positionOrder.isEmpty() )
2910 positionOrder = *DEFAULT_PLACEMENT_ORDER();
2916 if ( !dataDefinedOrder.isEmpty() )
2921 labelFeature->setPredefinedPositionOrder( positionOrder );
2924 labelFeature->setDataDefinedValues( dataDefinedValues );
2926 return labelFeature;
2934 if ( !obstacleGeometry.
isNull() )
2936 geom = obstacleGeometry;
2951 if ( ls->numPoints() < 2 )
2957 std::unique_ptr<QgsGeometry> scopedClonedGeom;
2963 geom = simplifier.
simplify( geom );
2967 std::unique_ptr<QgsGeometry> scopedPreparedGeom;
2975 if ( !geos_geom_clone )
2979 std::unique_ptr< QgsLabelFeature > obstacleFeature = std::make_unique< QgsLabelFeature >( f.
id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
2980 obstacleFeature->setFeature( f );
2985 obstacleFeature->setObstacleSettings( os );
2988 return obstacleFeature;
2991bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
2995 if ( !mDataDefinedProperties.
isActive( p ) )
2999 exprVal = mDataDefinedProperties.
value( p, context );
3006 bool bol = exprVal.toBool();
3007 dataDefinedValues.insert( p, QVariant( bol ) );
3013 int size = exprVal.toInt( &ok );
3017 dataDefinedValues.insert( p, QVariant( size ) );
3025 int size = exprVal.toInt( &ok );
3027 if ( ok && size > 0 )
3029 dataDefinedValues.insert( p, QVariant( size ) );
3037 double size = exprVal.toDouble( &ok );
3041 dataDefinedValues.insert( p, QVariant( size ) );
3049 double size = exprVal.toDouble( &ok );
3051 if ( ok && size > 0.0 )
3053 dataDefinedValues.insert( p, QVariant( size ) );
3061 double rot = exprVal.toDouble( &ok );
3064 if ( rot < -180.0 && rot >= -360 )
3068 if ( rot > 180.0 && rot <= 360 )
3072 if ( rot >= -180 && rot <= 180 )
3074 dataDefinedValues.insert( p, QVariant( rot ) );
3083 int size = exprVal.toInt( &ok );
3084 if ( ok && size >= 0 && size <= 100 )
3086 dataDefinedValues.insert( p, QVariant( size ) );
3093 QString
str = exprVal.toString();
3095 dataDefinedValues.insert( p, QVariant(
str ) );
3100 QString unitstr = exprVal.toString().trimmed();
3102 if ( !unitstr.isEmpty() )
3111 QString colorstr = exprVal.toString().trimmed();
3114 if ( color.isValid() )
3116 dataDefinedValues.insert( p, QVariant( color ) );
3123 QString joinstr = exprVal.toString().trimmed();
3125 if ( !joinstr.isEmpty() )
3134 QString blendstr = exprVal.toString().trimmed();
3136 if ( !blendstr.isEmpty() )
3149 dataDefinedValues.insert( p, res );
3160 dataDefinedValues.insert( p, res );
3170void QgsPalLayerSettings::parseTextStyle( QFont &labelFont,
3183 QString ddFontFamily;
3190 QString family = exprVal.toString().trimmed();
3191 QgsDebugMsgLevel( QStringLiteral(
"exprVal Font family:%1" ).arg( family ), 4 );
3194 if ( labelFont.family() != family )
3200 ddFontFamily = family;
3207 QString ddFontStyle;
3213 QString fontstyle = exprVal.toString().trimmed();
3214 QgsDebugMsgLevel( QStringLiteral(
"exprVal Font style:%1" ).arg( fontstyle ), 4 );
3215 ddFontStyle = fontstyle;
3220 bool ddBold =
false;
3228 bool ddItalic =
false;
3238 QFont appFont = QApplication::font();
3239 bool newFontBuilt =
false;
3240 if ( ddBold || ddItalic )
3243 newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3244 newFontBuilt =
true;
3245 newFont.setBold( ddBold );
3246 newFont.setItalic( ddItalic );
3248 else if ( !ddFontStyle.isEmpty()
3249 && ddFontStyle.compare( QLatin1String(
"Ignore" ), Qt::CaseInsensitive ) != 0 )
3251 if ( !ddFontFamily.isEmpty() )
3255 mFontDB = std::make_unique< QFontDatabase >();
3257 QFont styledfont = mFontDB->font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3258 if ( appFont != styledfont )
3260 newFont = styledfont;
3261 newFontBuilt =
true;
3268 else if ( !ddFontFamily.isEmpty() )
3270 if ( ddFontStyle.compare( QLatin1String(
"Ignore" ), Qt::CaseInsensitive ) != 0 )
3274 mFontDB = std::make_unique< QFontDatabase >();
3275 QFont styledfont = mFontDB->font( ddFontFamily, mFormat.
namedStyle(), appFont.pointSize() );
3276 if ( appFont != styledfont )
3278 newFont = styledfont;
3279 newFontBuilt =
true;
3284 newFont = QFont( ddFontFamily );
3285 newFontBuilt =
true;
3293 newFont.setPixelSize( labelFont.pixelSize() );
3294 newFont.setUnderline( labelFont.underline() );
3295 newFont.setStrikeOut( labelFont.strikeOut() );
3296 newFont.setWordSpacing( labelFont.wordSpacing() );
3297 newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3299 labelFont = newFont;
3303 double wordspace = labelFont.wordSpacing();
3312 double letterspace = labelFont.letterSpacing();
3325 labelFont.setStrikeOut( strikeout );
3340 labelFont.setUnderline( underline );
3366 drawBuffer = exprVal.toBool();
3379 double bufrSize = buffer.
size();
3382 bufrSize = exprVal.toDouble();
3386 double bufferOpacity = buffer.
opacity() * 100;
3389 bufferOpacity = exprVal.toDouble();
3392 drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufferOpacity > 0 );
3422 bool maskEnabled = mask.
enabled();
3425 maskEnabled = exprVal.toBool();
3434 double bufrSize = mask.
size();
3437 bufrSize = exprVal.toDouble();
3441 double opacity = mask.
opacity() * 100;
3444 opacity = exprVal.toDouble();
3447 maskEnabled = ( maskEnabled && bufrSize > 0.0 && opacity > 0 );
3472 wrapchr = exprVal.toString();
3478 evalAutoWrapLength = exprVal.toInt();
3491 QString
str = exprVal.toString().trimmed();
3494 if ( !
str.isEmpty() )
3499 if (
str.compare( QLatin1String(
"Center" ), Qt::CaseInsensitive ) == 0 )
3501 aligntype = Qgis::LabelMultiLineAlignment::Center;
3503 else if (
str.compare( QLatin1String(
"Right" ), Qt::CaseInsensitive ) == 0 )
3505 aligntype = Qgis::LabelMultiLineAlignment::Right;
3507 else if (
str.compare( QLatin1String(
"Follow" ), Qt::CaseInsensitive ) == 0 )
3509 aligntype = Qgis::LabelMultiLineAlignment::FollowPlacement;
3511 else if (
str.compare( QLatin1String(
"Justify" ), Qt::CaseInsensitive ) == 0 )
3513 aligntype = Qgis::LabelMultiLineAlignment::Justify;
3528 QString
str = exprVal.toString().trimmed();
3529 if ( !
str.isEmpty() )
3538 drawDirSymb = exprVal.toBool();
3553 QString
str = exprVal.toString().trimmed();
3556 if ( !
str.isEmpty() )
3561 if (
str.compare( QLatin1String(
"Above" ), Qt::CaseInsensitive ) == 0 )
3565 else if (
str.compare( QLatin1String(
"Below" ), Qt::CaseInsensitive ) == 0 )
3587 bool drawShape = background.
enabled();
3590 drawShape = exprVal.toBool();
3599 double shapeOpacity = background.
opacity() * 100;
3602 shapeOpacity = 100.0 * exprVal.toDouble();
3605 drawShape = ( drawShape && shapeOpacity > 0 );
3621 QString skind = exprVal.toString().trimmed();
3622 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeKind:%1" ).arg( skind ), 4 );
3624 if ( !skind.isEmpty() )
3633 QString svgPath = background.
svgFile();
3640 QString svgfile = exprVal.toString().trimmed();
3641 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3656 QString stype = exprVal.toString().trimmed();
3657 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3659 if ( !stype.isEmpty() )
3668 double ddShpSizeX = background.
size().width();
3671 ddShpSizeX = exprVal.toDouble();
3675 double ddShpSizeY = background.
size().height();
3678 ddShpSizeY = exprVal.toDouble();
3684 && ( svgPath.isEmpty()
3685 || ( !svgPath.isEmpty()
3687 && ddShpSizeX == 0.0 ) ) )
3695 && ddShpSizeX == 0.0 ) ) )
3702 && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3727 QString rotstr = exprVal.toString().trimmed();
3728 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3730 if ( !rotstr.isEmpty() )
3781 bool drawShadow = shadow.
enabled();
3784 drawShadow = exprVal.toBool();
3793 double shadowOpacity = shadow.
opacity() * 100;
3796 shadowOpacity = exprVal.toDouble();
3803 shadowOffDist = exprVal.toDouble();
3810 shadowRad = exprVal.toDouble();
3813 drawShadow = ( drawShadow && shadowOpacity > 0 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3830 QString
str = exprVal.toString().trimmed();
3833 if ( !
str.isEmpty() )
3868 switch ( layer->
type() )
3870 case Qgis::LayerType::Vector:
3872 const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer );
3876 case Qgis::LayerType::VectorTile:
3883 return !labeling->styles().empty();
3888 case Qgis::LayerType::Raster:
3889 case Qgis::LayerType::Plugin:
3890 case Qgis::LayerType::Mesh:
3891 case Qgis::LayerType::PointCloud:
3892 case Qgis::LayerType::Annotation:
3893 case Qgis::LayerType::Group:
3907 if ( geometry.
type() == Qgis::GeometryType::Line && geometry.
isMultipart() && mergeLines )
3926 if ( geometry.
type() == Qgis::GeometryType::Polygon && !geometry.
isGeosValid() )
3932QStringList
QgsPalLabeling::splitToLines(
const QString &text,
const QString &wrapCharacter,
const int autoWrapLength,
const bool useMaxLineLengthWhenAutoWrapping )
3934 QStringList multiLineSplit;
3935 if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String(
"\n" ) )
3938 const QStringList lines = text.split( wrapCharacter );
3939 for (
const QString &line : lines )
3941 multiLineSplit.append( line.split(
'\n' ) );
3946 multiLineSplit = text.split(
'\n' );
3950 if ( autoWrapLength != 0 )
3952 QStringList autoWrappedLines;
3953 autoWrappedLines.reserve( multiLineSplit.count() );
3954 for (
const QString &line : std::as_const( multiLineSplit ) )
3956 autoWrappedLines.append(
QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split(
'\n' ) );
3958 multiLineSplit = autoWrappedLines;
3960 return multiLineSplit;
3965 QStringList graphemes;
3966 QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3967 int currentBoundary = -1;
3968 int previousBoundary = 0;
3969 while ( ( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3971 graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3972 previousBoundary = currentBoundary;
3987 if ( geom.
type() == Qgis::GeometryType::Line && geom.
isMultipart() && mergeLines )
4002 QgsDebugMsgLevel( QStringLiteral(
"Ignoring feature due to transformation exception" ), 4 );
4008 return std::isfinite( point.
x() ) && std::isfinite( point.
y() );
4012 cp->removeInvalidRings();
4014 else if (
QgsMultiSurface *ms = qgsgeometry_cast< QgsMultiSurface * >( geom.
get() ) )
4016 for (
int i = 0; i < ms->numGeometries(); ++i )
4018 if (
QgsCurvePolygon *cp = qgsgeometry_cast< QgsCurvePolygon * >( ms->geometryN( i ) ) )
4019 cp->removeInvalidRings();
4036 const bool mustClip = ( !clipGeometry.
isNull() &&
4040 bool mustClipExact =
false;
4056 if ( geom.
type() == Qgis::GeometryType::Polygon )
4064 QVector< QgsGeometry> parts;
4065 parts.reserve( qgsgeometry_cast< const QgsGeometryCollection * >( geom.
constGet() )->numGeometries() );
4074 parts.append( partGeom );
4082 if ( bufferGeom.
isNull() )
4091 if ( mustClipExact )
4118 if ( featureType == Qgis::GeometryType::Point )
4124 if ( featureType == Qgis::GeometryType::Line )
4126 double length = geom.
length();
4127 if ( length >= 0.0 )
4129 return ( length >= ( minSize * mapUnitsPerMM ) );
4132 else if ( featureType == Qgis::GeometryType::Polygon )
4134 double area = geom.
area();
4137 return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
4145 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4148 bool changed =
false;
4154 format.
setColor( ddColor.value<QColor>() );
4179 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4241 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4244 bool changed =
false;
4292 buffer.
setColor( ddColor.value<QColor>() );
4319 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4321 if ( ddValues.isEmpty() )
4325 bool changed =
false;
4385 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4388 bool changed =
false;
4428 QSizeF size = background.
size();
4435 QSizeF size = background.
size();
4536 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4539 bool changed =
false;
4598 shadow.
setColor( ddColor.value<QColor>() );
4638 double cx = lp->
getX() + w / 2.0;
4639 double cy = lp->
getY() + h / 2.0;
4642 double sw = w * scale;
4643 double sh = h * scale;
4644 QRectF rect( -sw / 2, -sh / 2, sw, sh );
4646 painter->translate( xform->
transform( QPointF( cx, cy ) ).toQPointF() );
4650 if ( lp->
getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4651 lp->
getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
4654 painter->rotate( rotation );
4657 painter->translate( rect.bottomLeft() );
4658 painter->rotate( -lp->
getAlpha() * 180 / M_PI );
4659 painter->translate( -rect.bottomLeft() );
4662 QRectF rect( 0, 0, outPt2.
x() - outPt.
x(), outPt2.
y() - outPt.
y() );
4663 painter->translate( QPointF( outPt.
x(), outPt.
y() ) );
4664 painter->rotate( -lp->
getAlpha() * 180 / M_PI );
4669 painter->setPen( QColor( 255, 0, 0, 64 ) );
4673 painter->setPen( QColor( 0, 0, 0, 64 ) );
4675 painter->drawRect( rect );
4679 rect.moveTo( outPt.
x(), outPt.
y() );
@ Success
Operation succeeded.
AngleUnit
Units of angles.
LabelOffsetType
Behavior modifier for label offset and distance, only applies in some label placement modes.
@ FromPoint
Offset distance applies from point geometry.
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
@ AllowPlacementInsideOfPolygon
Labels can be placed inside a polygon feature.
@ AllowPlacementOutsideOfPolygon
Labels can be placed outside of a polygon feature.
@ Warning
Warning message.