23  const QString skind = 
string.trimmed();
 
   25  if ( skind.compare( QLatin1String( 
"Square" ), Qt::CaseInsensitive ) == 0 )
 
   29  else if ( skind.compare( QLatin1String( 
"Ellipse" ), Qt::CaseInsensitive ) == 0 )
 
   33  else if ( skind.compare( QLatin1String( 
"Circle" ), Qt::CaseInsensitive ) == 0 )
 
   37  else if ( skind.compare( QLatin1String( 
"SVG" ), Qt::CaseInsensitive ) == 0 )
 
   41  else if ( skind.compare( QLatin1String( 
"marker" ), Qt::CaseInsensitive ) == 0 )
 
 
   50  const QString stype = 
string.trimmed();
 
   54  if ( stype.compare( QLatin1String( 
"Fixed" ), Qt::CaseInsensitive ) == 0 )
 
 
   63  const QString rotstr = 
string.trimmed();
 
   67  if ( rotstr.compare( QLatin1String( 
"Offset" ), Qt::CaseInsensitive ) == 0 )
 
   71  else if ( rotstr.compare( QLatin1String( 
"Fixed" ), Qt::CaseInsensitive ) == 0 )
 
 
   80  const QString 
str = 
string.trimmed();
 
   84  if ( 
str.compare( QLatin1String( 
"Text" ), Qt::CaseInsensitive ) == 0 )
 
   88  else if ( 
str.compare( QLatin1String( 
"Buffer" ), Qt::CaseInsensitive ) == 0 )
 
   92  else if ( 
str.compare( QLatin1String( 
"Background" ), Qt::CaseInsensitive ) == 0 )
 
 
  101  switch ( orientation )
 
  104      return QStringLiteral( 
"horizontal" );
 
  106      return QStringLiteral( 
"vertical" );
 
  108      return QStringLiteral( 
"rotation-based" );
 
 
  118  const QString cleaned = name.toLower().trimmed();
 
  120  if ( cleaned == QLatin1String( 
"horizontal" ) )
 
  122  else if ( cleaned == QLatin1String( 
"vertical" ) )
 
  124  else if ( cleaned == QLatin1String( 
"rotation-based" ) )
 
 
  148  const int r = layer->
customProperty( property + 
'R', QVariant( defaultColor.red() ) ).toInt();
 
  149  const int g = layer->
customProperty( property + 
'G', QVariant( defaultColor.green() ) ).toInt();
 
  150  const int b = layer->
customProperty( property + 
'B', QVariant( defaultColor.blue() ) ).toInt();
 
  151  const int a = withAlpha ? layer->
customProperty( property + 
'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
 
  152  return QColor( r, g, b, a );
 
 
  157  const int numPoints = line.size();
 
  158  std::vector<double> pathDistances( numPoints );
 
  160  const QPointF *p = line.data();
 
  163  pathDistances[0] = 0;
 
  164  double prevX = p->x();
 
  165  double prevY = p->y();
 
  168  std::vector< double > x( numPoints );
 
  169  std::vector< double > y( numPoints );
 
  173  for ( 
int i = 1; i < numPoints; ++i )
 
  177    pathDistances[i] = std::sqrt( dx * dx + dy * dy );
 
  186  return generateCurvedTextPlacementPrivate( metrics, x.data(), y.data(), numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, 
false );
 
 
  191  return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle );
 
 
  194std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacementPrivate( 
const QgsPrecalculatedTextMetrics &metrics, 
const double *x, 
const double *y, 
int numPoints, 
const std::vector<double> &pathDistances, 
double offsetAlongLine, LabelLineDirection direction, CurvedTextFlags flags, 
double maxConcaveAngle, 
double maxConvexAngle, 
bool isSecondAttempt )
 
  196  std::unique_ptr< CurvePlacementProperties > output = std::make_unique< CurvePlacementProperties >();
 
  197  output->graphemePlacement.reserve( metrics.
count() );
 
  199  double offsetAlongSegment = offsetAlongLine;
 
  202  while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
 
  204    offsetAlongSegment -= pathDistances[index];
 
  207  if ( index >= numPoints )
 
  212  const double segmentLength = pathDistances[index];
 
  219  int characterCount = metrics.
count();
 
  225    double distance = offsetAlongSegment;
 
  226    int endindex = index;
 
  228    double startLabelX = 0;
 
  229    double startLabelY = 0;
 
  230    double endLabelX = 0;
 
  231    double endLabelY = 0;
 
  232    for ( 
int i = 0; i < characterCount; i++ )
 
  235      double characterStartX, characterStartY;
 
  236      if ( !nextCharPosition( characterWidth, pathDistances[endindex], x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
 
  240          characterCount = i + 1;
 
  250        startLabelX = characterStartX;
 
  251        startLabelY = characterStartY;
 
  256    const double dx = endLabelX - startLabelX;
 
  257    const double dy = endLabelY - startLabelY;
 
  258    const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
 
  260    if ( lineAngle > 90 || lineAngle < -90 )
 
  262      output->labeledLineSegmentIsRightToLeft = 
true;
 
  266  if ( isSecondAttempt )
 
  270    output->labeledLineSegmentIsRightToLeft = 
false;
 
  271    output->flippedCharacterPlacementToGetUprightLabels = 
true;
 
  274  const double dx = x[index] - x[index - 1];
 
  275  const double dy = y[index] - y[index - 1];
 
  277  double angle = std::atan2( -dy, dx );
 
  282  for ( 
int i = 0; i < characterCount; i++ )
 
  284    const double lastCharacterAngle = 
angle;
 
  287    const double characterWidth = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterWidth( i ) : metrics.
characterWidth( characterCount - i - 1 );
 
  292    const double characterHeight = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterHeight( i ) : metrics.
characterHeight( characterCount - i - 1 );
 
  293    const double characterDescent = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterDescent( i ) : metrics.
characterDescent( characterCount - i - 1 );
 
  295    double characterStartX = 0;
 
  296    double characterStartY = 0;
 
  297    double characterEndX = 0;
 
  298    double characterEndY = 0;
 
  299    if ( !nextCharPosition( characterWidth, pathDistances[index], x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY ) )
 
  303        characterCount = i + 1;
 
  308        output->graphemePlacement.clear();
 
  314    angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
 
  316    if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
 
  321      double angleDelta = lastCharacterAngle - 
angle;
 
  323      while ( angleDelta > M_PI )
 
  324        angleDelta -= 2 * M_PI;
 
  325      while ( angleDelta < -M_PI )
 
  326        angleDelta += 2 * M_PI;
 
  327      if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
 
  329        output->graphemePlacement.clear();
 
  338      double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
 
  339      if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  343      characterStartX += dist * std::cos( angle + M_PI_2 );
 
  344      characterStartY -= dist * std::sin( angle + M_PI_2 );
 
  347    double renderAngle = 
angle;
 
  348    CurvedGraphemePlacement placement;
 
  349    placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
 
  350    placement.x = characterStartX;
 
  351    placement.y = characterStartY;
 
  352    placement.width = characterWidth;
 
  353    placement.height = characterHeight;
 
  354    if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  357      placement.x += characterWidth * std::cos( renderAngle );
 
  358      placement.y -= characterWidth * std::sin( renderAngle );
 
  361    placement.angle = -renderAngle;
 
  362    output->graphemePlacement.push_back( placement );
 
  365    while ( renderAngle >= 2 * M_PI )
 
  366      renderAngle -= 2 * M_PI;
 
  367    while ( renderAngle < 0 )
 
  368      renderAngle += 2 * M_PI;
 
  370    if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
 
  371      output->upsideDownCharCount++;
 
  378    return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, 
true );
 
  384bool 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 )
 
  393  double segmentStartX = x[index - 1];
 
  394  double segmentStartY = y[index - 1];
 
  396  double segmentEndX = x[index];
 
  397  double segmentEndY = y[index];
 
  399  const double segmentDx = segmentEndX - segmentStartX;
 
  400  const double segmentDy = segmentEndY - segmentStartY;
 
  402  characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  403  characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  409  if ( segmentLength - currentDistanceAlongSegment >= charWidth )
 
  412    currentDistanceAlongSegment += charWidth;
 
  413    characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  414    characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  422      segmentStartX = segmentEndX;
 
  423      segmentStartY = segmentEndY;
 
  425      if ( index >= numPoints ) 
 
  429      segmentEndX = x[index];
 
  430      segmentEndY = y[index];
 
  432    while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); 
 
  435    findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
 
  438    currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
 
  443void QgsTextRendererUtils::findLineCircleIntersection( 
double cx, 
double cy, 
double radius, 
double x1, 
double y1, 
double x2, 
double y2, 
double &xRes, 
double &yRes )
 
  445  double multiplier = 1;
 
  457    radius *= multiplier;
 
  460  const double dx = x2 - x1;
 
  461  const double dy = y2 - y1;
 
  463  const double A = dx * dx + dy * dy;
 
  464  const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
 
  467  const double det = B * B - 4 * A * C;
 
  468  if ( A <= 0.000000000001 || det < 0 )
 
  475    const double t = -B / ( 2 * A );
 
  484    const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
 
  489  if ( multiplier != 1 )
 
TextOrientation
Text orientations.
 
@ Vertical
Vertically oriented text.
 
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling)
 
@ Horizontal
Horizontally oriented text.
 
RenderUnit
Rendering size units.
 
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
 
@ Millimeters
Millimeters.
 
@ Points
Points (e.g., for font sizes)
 
static double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
 
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.
 
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 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.
 
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
 
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
 
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
 
static Qgis::RenderUnit convertFromOldLabelUnit(int val)
Converts a unit from an old (pre 3.0) label unit.
 
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
 
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, CurvedTextFlags flags=CurvedTextFlags())
Calculates curved text placement properties.
 
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.
 
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)