22  const QString skind = 
string.trimmed();
 
   24  if ( skind.compare( QLatin1String( 
"Square" ), Qt::CaseInsensitive ) == 0 )
 
   28  else if ( skind.compare( QLatin1String( 
"Ellipse" ), Qt::CaseInsensitive ) == 0 )
 
   32  else if ( skind.compare( QLatin1String( 
"Circle" ), Qt::CaseInsensitive ) == 0 )
 
   36  else if ( skind.compare( QLatin1String( 
"SVG" ), Qt::CaseInsensitive ) == 0 )
 
   40  else if ( skind.compare( QLatin1String( 
"marker" ), Qt::CaseInsensitive ) == 0 )
 
   49  const QString stype = 
string.trimmed();
 
   53  if ( stype.compare( QLatin1String( 
"Fixed" ), Qt::CaseInsensitive ) == 0 )
 
   62  const QString rotstr = 
string.trimmed();
 
   66  if ( rotstr.compare( QLatin1String( 
"Offset" ), Qt::CaseInsensitive ) == 0 )
 
   70  else if ( rotstr.compare( QLatin1String( 
"Fixed" ), Qt::CaseInsensitive ) == 0 )
 
   79  const QString 
str = 
string.trimmed();
 
   83  if ( 
str.compare( QLatin1String( 
"Text" ), Qt::CaseInsensitive ) == 0 )
 
   87  else if ( 
str.compare( QLatin1String( 
"Buffer" ), Qt::CaseInsensitive ) == 0 )
 
   91  else if ( 
str.compare( QLatin1String( 
"Background" ), Qt::CaseInsensitive ) == 0 )
 
  100  switch ( orientation )
 
  102    case Qgis::TextOrientation::Horizontal:
 
  103      return QStringLiteral( 
"horizontal" );
 
  104    case Qgis::TextOrientation::Vertical:
 
  105      return QStringLiteral( 
"vertical" );
 
  106    case Qgis::TextOrientation::RotationBased:
 
  107      return QStringLiteral( 
"rotation-based" );
 
  117  const QString cleaned = name.toLower().trimmed();
 
  119  if ( cleaned == QLatin1String( 
"horizontal" ) )
 
  120    return Qgis::TextOrientation::Horizontal;
 
  121  else if ( cleaned == QLatin1String( 
"vertical" ) )
 
  122    return Qgis::TextOrientation::Vertical;
 
  123  else if ( cleaned == QLatin1String( 
"rotation-based" ) )
 
  124    return Qgis::TextOrientation::RotationBased;
 
  128  return Qgis::TextOrientation::Horizontal;
 
  147  const int r = layer->
customProperty( property + 
'R', QVariant( defaultColor.red() ) ).toInt();
 
  148  const int g = layer->
customProperty( property + 
'G', QVariant( defaultColor.green() ) ).toInt();
 
  149  const int b = layer->
customProperty( property + 
'B', QVariant( defaultColor.blue() ) ).toInt();
 
  150  const int a = withAlpha ? layer->
customProperty( property + 
'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
 
  151  return QColor( r, g, b, a );
 
  158  std::vector<double> pathDistances( numPoints );
 
  160  const double *x = line->
xData();
 
  161  const double *y = line->
yData();
 
  164  pathDistances[0] = 0;
 
  168  for ( 
int i = 1; i < numPoints; ++i )
 
  172    pathDistances[i] = std::sqrt( dx * dx + dy * dy );
 
  178  return generateCurvedTextPlacementPrivate( metrics, line->
xData(), line->
yData(), numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
 
  184  return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
 
  187QgsTextRendererUtils::CurvePlacementProperties *QgsTextRendererUtils::generateCurvedTextPlacementPrivate( 
const QgsPrecalculatedTextMetrics &metrics, 
const double *x, 
const double *y, 
int numPoints, 
const std::vector<double> &pathDistances, 
double offsetAlongLine, LabelLineDirection direction, 
double maxConcaveAngle, 
double maxConvexAngle, 
bool uprightOnly, 
bool isSecondAttempt )
 
  189  std::unique_ptr< CurvePlacementProperties > output = std::make_unique< CurvePlacementProperties >();
 
  190  output->graphemePlacement.reserve( metrics.
count() );
 
  192  double offsetAlongSegment = offsetAlongLine;
 
  195  while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
 
  197    offsetAlongSegment -= pathDistances[index];
 
  200  if ( index >= numPoints )
 
  202    return output.release();
 
  205  const double segmentLength = pathDistances[index];
 
  209    return output.release();
 
  212  const int characterCount = metrics.
count();
 
  218    double distance = offsetAlongSegment;
 
  219    int endindex = index;
 
  221    double startLabelX = 0;
 
  222    double startLabelY = 0;
 
  223    double endLabelX = 0;
 
  224    double endLabelY = 0;
 
  225    for ( 
int i = 0; i < characterCount; i++ )
 
  228      double characterStartX, characterStartY;
 
  229      if ( !nextCharPosition( characterWidth, pathDistances[endindex], x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
 
  231        return output.release();
 
  235        startLabelX = characterStartX;
 
  236        startLabelY = characterStartY;
 
  241    const double dx = endLabelX - startLabelX;
 
  242    const double dy = endLabelY - startLabelY;
 
  243    const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
 
  245    if ( lineAngle > 90 || lineAngle < -90 )
 
  247      output->labeledLineSegmentIsRightToLeft = 
true;
 
  251  if ( isSecondAttempt )
 
  255    output->labeledLineSegmentIsRightToLeft = 
false;
 
  256    output->flippedCharacterPlacementToGetUprightLabels = 
true;
 
  259  const double dx = x[index] - x[index - 1];
 
  260  const double dy = y[index] - y[index - 1];
 
  262  double angle = std::atan2( -dy, dx );
 
  267  for ( 
int i = 0; i < characterCount; i++ )
 
  269    const double lastCharacterAngle = 
angle;
 
  272    const double characterWidth = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterWidth( i ) : metrics.
characterWidth( characterCount - i - 1 );
 
  277    const double characterHeight = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterHeight( i ) : metrics.
characterHeight( characterCount - i - 1 );
 
  278    const double characterDescent = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterDescent( i ) : metrics.
characterDescent( characterCount - i - 1 );
 
  280    double characterStartX = 0;
 
  281    double characterStartY = 0;
 
  282    double characterEndX = 0;
 
  283    double characterEndY = 0;
 
  284    if ( !nextCharPosition( characterWidth, pathDistances[index], x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY ) )
 
  286      output->graphemePlacement.clear();
 
  287      return output.release();
 
  291    angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
 
  293    if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
 
  298      double angleDelta = lastCharacterAngle - 
angle;
 
  300      while ( angleDelta > M_PI )
 
  301        angleDelta -= 2 * M_PI;
 
  302      while ( angleDelta < -M_PI )
 
  303        angleDelta += 2 * M_PI;
 
  304      if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
 
  306        output->graphemePlacement.clear();
 
  307        return output.release();
 
  313    double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
 
  314    if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  318    characterStartX += dist * std::cos( 
angle + M_PI_2 );
 
  319    characterStartY -= dist * std::sin( 
angle + M_PI_2 );
 
  321    double renderAngle = 
angle;
 
  322    CurvedGraphemePlacement placement;
 
  323    placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
 
  324    placement.x = characterStartX;
 
  325    placement.y = characterStartY;
 
  326    placement.width = characterWidth;
 
  327    placement.height = characterHeight;
 
  328    if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  331      placement.x += characterWidth * std::cos( renderAngle );
 
  332      placement.y -= characterWidth * std::sin( renderAngle );
 
  335    placement.angle = -renderAngle;
 
  336    output->graphemePlacement.push_back( placement );
 
  339    while ( renderAngle >= 2 * M_PI )
 
  340      renderAngle -= 2 * M_PI;
 
  341    while ( renderAngle < 0 )
 
  342      renderAngle += 2 * M_PI;
 
  344    if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
 
  345      output->upsideDownCharCount++;
 
  348  if ( !isSecondAttempt && uprightOnly && output->upsideDownCharCount >= characterCount / 2.0 )
 
  352    return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly, 
true );
 
  355  return output.release();
 
  358bool QgsTextRendererUtils::nextCharPosition( 
double charWidth, 
double segmentLength, 
const double *x, 
const double *y, 
int numPoints, 
int &index, 
double ¤tDistanceAlongSegment, 
double &characterStartX, 
double &characterStartY, 
double &characterEndX, 
double &characterEndY )
 
  367  double segmentStartX = x[index - 1];
 
  368  double segmentStartY = y[index - 1];
 
  370  double segmentEndX = x[index];
 
  371  double segmentEndY = y[index];
 
  373  const double segmentDx = segmentEndX - segmentStartX;
 
  374  const double segmentDy = segmentEndY - segmentStartY;
 
  376  characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  377  characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  383  if ( segmentLength - currentDistanceAlongSegment >= charWidth )
 
  386    currentDistanceAlongSegment += charWidth;
 
  387    characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  388    characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  396      segmentStartX = segmentEndX;
 
  397      segmentStartY = segmentEndY;
 
  399      if ( index >= numPoints ) 
 
  403      segmentEndX = x[index];
 
  404      segmentEndY = y[index];
 
  406    while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); 
 
  409    findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
 
  412    currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
 
  417void QgsTextRendererUtils::findLineCircleIntersection( 
double cx, 
double cy, 
double radius, 
double x1, 
double y1, 
double x2, 
double y2, 
double &xRes, 
double &yRes )
 
  419  double multiplier = 1;
 
  431    radius *= multiplier;
 
  434  const double dx = x2 - x1;
 
  435  const double dy = y2 - y1;
 
  437  const double A = dx * dx + dy * dy;
 
  438  const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
 
  439  const double C = ( x1 - cx ) * ( x1 - cx ) + ( y1 - cy ) * ( y1 - cy ) - radius * radius;
 
  441  const double det = B * B - 4 * A * C;
 
  442  if ( A <= 0.000000000001 || det < 0 )
 
  449    const double t = -B / ( 2 * A );
 
  458    const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
 
  463  if ( multiplier != 1 )
 
TextOrientation
Text orientations.
 
Line string geometry type, with support for z-dimension and m-values.
 
const double * yData() const
Returns a const pointer to the y vertex data.
 
const double * xData() const
Returns a const pointer to the x vertex data.
 
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
 
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
 
Contains precalculated properties regarding text metrics for text to be renderered at a later stage.
 
double maximumCharacterHeight() const
Returns the maximum height of any character found in the text.
 
double characterDescent(int position) const
Returns the descent of the character at the specified position.
 
int count() const
Returns the total number of characters.
 
double maximumCharacterDescent() const
Returns the maximum descent of any character found in the text.
 
double characterWidth(int position) const
Returns the width of the character at the specified position.
 
double characterHeight(int position) const
Returns the character height of the character at the specified position (actually font metrics height...
 
SizeType
Methods for determining the background shape size.
 
@ SizeBuffer
Shape size is determined by adding a buffer margin around text.
 
ShapeType
Background shape types.
 
@ ShapeMarkerSymbol
Marker symbol.
 
@ ShapeSquare
Square - buffered sizes only.
 
@ ShapeRectangle
Rectangle.
 
RotationType
Methods for determining the rotation of the background shape.
 
@ RotationOffset
Shape rotation is offset from text rotation.
 
@ RotationSync
Shape rotation is synced with text rotation.
 
@ RotationFixed
Shape rotation is a fixed angle.
 
Contains placement information for a curved text layout.
 
static QgsTextBackgroundSettings::ShapeType decodeShapeType(const QString &string)
Decodes a string representation of a background shape type to a type.
 
static Qgis::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
 
LabelLineDirection
Controls behavior of curved text with respect to line directions.
 
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
 
static QColor readColor(QgsVectorLayer *layer, const QString &property, const QColor &defaultColor=Qt::black, bool withAlpha=true)
Converts an encoded color value from a layer property.
 
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType(const QString &string)
Decodes a string representation of a shadow placement type to a type.
 
static CurvePlacementProperties * generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const double *x, const double *y, int numPoints, const std::vector< double > &pathDistances, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, bool uprightOnly=true)
Calculates curved text placement properties.
 
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType(const QString &string)
Decodes a string representation of a background rotation type to a type.
 
static QString encodeTextOrientation(Qgis::TextOrientation orientation)
Encodes a text orientation.
 
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType(const QString &string)
Decodes a string representation of a background size type to a type.
 
static QgsUnitTypes::RenderUnit convertFromOldLabelUnit(int val)
Converts a unit from an old (pre 3.0) label unit.
 
ShadowPlacement
Placement positions for text shadow.
 
@ ShadowBuffer
Draw shadow under buffer.
 
@ ShadowShape
Draw shadow under background shape.
 
@ ShadowLowest
Draw shadow below all text components.
 
@ ShadowText
Draw shadow under text.
 
RenderUnit
Rendering size units.
 
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
 
@ RenderPoints
Points (e.g., for font sizes)
 
@ RenderMillimeters
Millimeters.
 
@ RenderMapUnits
Map units.
 
Represents a vector layer which manages a vector based data sets.
 
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)