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   QString cleaned = name.toLower().trimmed();
 
  120   if ( cleaned == QLatin1String( 
"horizontal" ) )
 
  122   else if ( cleaned == QLatin1String( 
"vertical" ) )
 
  124   else if ( cleaned == QLatin1String( 
"rotation-based" ) )
 
  148   int r = layer->
customProperty( property + 
'R', QVariant( defaultColor.red() ) ).toInt();
 
  149   int g = layer->
customProperty( property + 
'G', QVariant( defaultColor.green() ) ).toInt();
 
  150   int b = layer->
customProperty( property + 
'B', QVariant( defaultColor.blue() ) ).toInt();
 
  151   int a = withAlpha ? layer->
customProperty( property + 
'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
 
  152   return QColor( r, g, b, a );
 
  159   std::vector<double> pathDistances( numPoints );
 
  161   const double *x = line->
xData();
 
  162   const double *y = line->
yData();
 
  165   pathDistances[0] = 0;
 
  169   for ( 
int i = 1; i < numPoints; ++i )
 
  173     pathDistances[i] = std::sqrt( dx * dx + dy * dy );
 
  179   return generateCurvedTextPlacementPrivate( metrics, line->
xData(), line->
yData(), numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
 
  185   return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
 
  188 QgsTextRendererUtils::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 )
 
  190   std::unique_ptr< CurvePlacementProperties > output = std::make_unique< CurvePlacementProperties >();
 
  191   output->graphemePlacement.reserve( metrics.
count() );
 
  193   double offsetAlongSegment = offsetAlongLine;
 
  196   while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
 
  198     offsetAlongSegment -= pathDistances[index];
 
  201   if ( index >= numPoints )
 
  203     return output.release();
 
  208   const double segmentLength = pathDistances[index];
 
  212     return output.release();
 
  215   const int characterCount = metrics.
count();
 
  221     double distance = offsetAlongSegment;
 
  222     int endindex = index;
 
  224     double startLabelX = 0;
 
  225     double startLabelY = 0;
 
  226     double endLabelX = 0;
 
  227     double endLabelY = 0;
 
  228     for ( 
int i = 0; i < characterCount; i++ )
 
  231       double characterStartX, characterStartY;
 
  232       if ( !nextCharPosition( characterWidth, pathDistances[endindex], x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
 
  234         return output.release();
 
  238         startLabelX = characterStartX;
 
  239         startLabelY = characterStartY;
 
  244     double dx = endLabelX - startLabelX;
 
  245     double dy = endLabelY - startLabelY;
 
  246     const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
 
  248     if ( lineAngle > 90 || lineAngle < -90 )
 
  250       output->labeledLineSegmentIsRightToLeft = 
true;
 
  254   if ( isSecondAttempt )
 
  258     output->labeledLineSegmentIsRightToLeft = 
false;
 
  259     output->flippedCharacterPlacementToGetUprightLabels = 
true;
 
  262   double dx = x[index] - x[index - 1];
 
  263   double dy = y[index] - y[index - 1];
 
  265   double angle = std::atan2( -dy, dx );
 
  267   for ( 
int i = 0; i < characterCount; i++ )
 
  269     double lastCharacterAngle = 
angle;
 
  272     const double characterWidth = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterWidth( i ) : metrics.
characterWidth( characterCount - i - 1 );
 
  277     double characterStartX = 0;
 
  278     double characterStartY = 0;
 
  279     double characterEndX = 0;
 
  280     double characterEndY = 0;
 
  281     if ( !nextCharPosition( characterWidth, pathDistances[index], x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY ) )
 
  283       output->graphemePlacement.clear();
 
  284       return output.release();
 
  288     angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
 
  290     if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
 
  295       double angleDelta = lastCharacterAngle - 
angle;
 
  297       while ( angleDelta > M_PI )
 
  298         angleDelta -= 2 * M_PI;
 
  299       while ( angleDelta < -M_PI )
 
  300         angleDelta += 2 * M_PI;
 
  301       if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
 
  303         output->graphemePlacement.clear();
 
  304         return output.release();
 
  311     if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  315     characterStartX += dist * std::cos( 
angle + M_PI_2 );
 
  316     characterStartY -= dist * std::sin( 
angle + M_PI_2 );
 
  318     double renderAngle = 
angle;
 
  319     CurvedGraphemePlacement placement;
 
  320     placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
 
  321     placement.x = characterStartX;
 
  322     placement.y = characterStartY;
 
  323     placement.width = characterWidth;
 
  324     placement.height = characterHeight;
 
  325     if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  328       placement.x += characterWidth * std::cos( renderAngle );
 
  329       placement.y -= characterWidth * std::sin( renderAngle );
 
  332     placement.angle = -renderAngle;
 
  333     output->graphemePlacement.push_back( placement );
 
  336     while ( renderAngle >= 2 * M_PI )
 
  337       renderAngle -= 2 * M_PI;
 
  338     while ( renderAngle < 0 )
 
  339       renderAngle += 2 * M_PI;
 
  341     if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
 
  342       output->upsideDownCharCount++;
 
  345   if ( !isSecondAttempt && uprightOnly && output->upsideDownCharCount >= characterCount / 2.0 )
 
  349     return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly, 
true );
 
  352   return output.release();
 
  355 bool 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 )
 
  364   double segmentStartX = x[index - 1];
 
  365   double segmentStartY = y[index - 1];
 
  367   double segmentEndX = x[index];
 
  368   double segmentEndY = y[index];
 
  370   double segmentDx = segmentEndX - segmentStartX;
 
  371   double segmentDy = segmentEndY - segmentStartY;
 
  373   characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  374   characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  380   if ( segmentLength - currentDistanceAlongSegment >= charWidth )
 
  383     currentDistanceAlongSegment += charWidth;
 
  384     characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  385     characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  393       segmentStartX = segmentEndX;
 
  394       segmentStartY = segmentEndY;
 
  396       if ( index >= numPoints ) 
 
  400       segmentEndX = x[index];
 
  401       segmentEndY = y[index];
 
  403     while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); 
 
  406     findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
 
  409     currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
 
  414 void QgsTextRendererUtils::findLineCircleIntersection( 
double cx, 
double cy, 
double radius, 
double x1, 
double y1, 
double x2, 
double y2, 
double &xRes, 
double &yRes )
 
  416   double multiplier = 1;
 
  428     radius *= multiplier;
 
  434   double A = dx * dx + dy * dy;
 
  435   double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
 
  436   double C = ( x1 - cx ) * ( x1 - cx ) + ( y1 - cy ) * ( y1 - cy ) - radius * radius;
 
  438   double det = B * B - 4 * A * C;
 
  439   if ( A <= 0.000000000001 || det < 0 )
 
  446     double t = -B / ( 2 * A );
 
  455     double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
 
  460   if ( multiplier != 1 )
 
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.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
const double * xData() const
Returns a const pointer to the x vertex data.
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 characterHeight() const
Character height (actually font metrics height, not individual character height).
int count() const
Returns the total number of characters.
double characterWidth(int position) const
Returns the width of the character at the specified position.
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.
TextOrientation
Text orientation.
@ HorizontalOrientation
Vertically oriented text.
@ RotationBasedOrientation
Horizontally or vertically oriented text based on rotation (only available for map labeling)
@ VerticalOrientation
Horizontally oriented text.
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 QString encodeTextOrientation(QgsTextFormat::TextOrientation orientation)
Encodes 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 QgsTextFormat::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
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 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)