QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgstextrendererutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstextrendererutils.h
3  -----------------
4  begin : May 2020
5  copyright : (C) Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgstextrendererutils.h"
17 #include "qgsvectorlayer.h"
18 #include "qgslinestring.h"
19 
21 {
23  const QString skind = string.trimmed();
24 
25  if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
26  {
28  }
29  else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
30  {
32  }
33  else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
34  {
36  }
37  else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
38  {
40  }
41  else if ( skind.compare( QLatin1String( "marker" ), Qt::CaseInsensitive ) == 0 )
42  {
44  }
45  return shpkind;
46 }
47 
49 {
50  const QString stype = string.trimmed();
51  // "Buffer"
53 
54  if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
55  {
57  }
58  return sizType;
59 }
60 
62 {
63  const QString rotstr = string.trimmed();
64  // "Sync"
66 
67  if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
68  {
70  }
71  else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
72  {
74  }
75  return rottype;
76 }
77 
79 {
80  const QString str = string.trimmed();
81  // "Lowest"
83 
84  if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
85  {
87  }
88  else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
89  {
91  }
92  else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
93  {
95  }
96  return shdwtype;
97 }
98 
100 {
101  switch ( orientation )
102  {
104  return QStringLiteral( "horizontal" );
106  return QStringLiteral( "vertical" );
108  return QStringLiteral( "rotation-based" );
109  }
110  return QString();
111 }
112 
114 {
115  if ( ok )
116  *ok = true;
117 
118  const QString cleaned = name.toLower().trimmed();
119 
120  if ( cleaned == QLatin1String( "horizontal" ) )
122  else if ( cleaned == QLatin1String( "vertical" ) )
124  else if ( cleaned == QLatin1String( "rotation-based" ) )
126 
127  if ( ok )
128  *ok = false;
130 }
131 
133 {
134  if ( val == 0 )
136  else if ( val == 1 )
138  else if ( val == 2 )
140  else if ( val == 3 )
142  else
144 }
145 
146 QColor QgsTextRendererUtils::readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor, bool withAlpha )
147 {
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 );
153 }
154 
155 #if 0
156 QgsTextRendererUtils::CurvePlacementProperties *QgsTextRendererUtils::generateCurvedTextPlacement( const QgsPrecalculatedTextMetrics &metrics, const QgsLineString *line, double offsetAlongLine, LabelLineDirection direction, double maxConcaveAngle, double maxConvexAngle, bool uprightOnly )
157 {
158  const int numPoints = line->numPoints();
159  std::vector<double> pathDistances( numPoints );
160 
161  const double *x = line->xData();
162  const double *y = line->yData();
163  double dx, dy;
164 
165  pathDistances[0] = 0;
166  double prevX = *x++;
167  double prevY = *y++;
168 
169  for ( int i = 1; i < numPoints; ++i )
170  {
171  dx = *x - prevX;
172  dy = *y - prevY;
173  pathDistances[i] = std::sqrt( dx * dx + dy * dy );
174 
175  prevX = *x++;
176  prevY = *y++;
177  }
178 
179  return generateCurvedTextPlacementPrivate( metrics, line->xData(), line->yData(), numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
180 }
181 #endif
182 
183 QgsTextRendererUtils::CurvePlacementProperties *QgsTextRendererUtils::generateCurvedTextPlacement( 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 )
184 {
185  return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
186 }
187 
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 )
189 {
190  std::unique_ptr< CurvePlacementProperties > output = std::make_unique< CurvePlacementProperties >();
191  output->graphemePlacement.reserve( metrics.count() );
192 
193  double offsetAlongSegment = offsetAlongLine;
194  int index = 1;
195  // Find index of segment corresponding to starting offset
196  while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
197  {
198  offsetAlongSegment -= pathDistances[index];
199  index += 1;
200  }
201  if ( index >= numPoints )
202  {
203  return output.release();
204  }
205 
206  const double characterHeight = metrics.characterHeight();
207 
208  const double segmentLength = pathDistances[index];
209  if ( qgsDoubleNear( segmentLength, 0.0 ) )
210  {
211  // Not allowed to place across on 0 length segments or discontinuities
212  return output.release();
213  }
214 
215  const int characterCount = metrics.count();
216 
217  if ( direction == RespectPainterOrientation && !isSecondAttempt )
218  {
219  // Calculate the orientation based on the angle of the path segment under consideration
220 
221  double distance = offsetAlongSegment;
222  int endindex = index;
223 
224  double startLabelX = 0;
225  double startLabelY = 0;
226  double endLabelX = 0;
227  double endLabelY = 0;
228  for ( int i = 0; i < characterCount; i++ )
229  {
230  const double characterWidth = metrics.characterWidth( i );
231  double characterStartX, characterStartY;
232  if ( !nextCharPosition( characterWidth, pathDistances[endindex], x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
233  {
234  return output.release();
235  }
236  if ( i == 0 )
237  {
238  startLabelX = characterStartX;
239  startLabelY = characterStartY;
240  }
241  }
242 
243  // Determine the angle of the path segment under consideration
244  const double dx = endLabelX - startLabelX;
245  const double dy = endLabelY - startLabelY;
246  const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
247 
248  if ( lineAngle > 90 || lineAngle < -90 )
249  {
250  output->labeledLineSegmentIsRightToLeft = true;
251  }
252  }
253 
254  if ( isSecondAttempt )
255  {
256  // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
257  // segment as left to right
258  output->labeledLineSegmentIsRightToLeft = false;
259  output->flippedCharacterPlacementToGetUprightLabels = true;
260  }
261 
262  const double dx = x[index] - x[index - 1];
263  const double dy = y[index] - y[index - 1];
264 
265  double angle = std::atan2( -dy, dx );
266 
267  for ( int i = 0; i < characterCount; i++ )
268  {
269  const double lastCharacterAngle = angle;
270 
271  // grab the next character according to the orientation
272  const double characterWidth = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.characterWidth( i ) : metrics.characterWidth( characterCount - i - 1 );
273  if ( qgsDoubleNear( characterWidth, 0.0 ) )
274  // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
275  continue;
276 
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 ) )
282  {
283  output->graphemePlacement.clear();
284  return output.release();
285  }
286 
287  // Calculate angle from the start of the character to the end based on start/end of character
288  angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
289 
290  if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
291  {
292  // Test lastCharacterAngle vs angle
293  // since our rendering angle has changed then check against our
294  // max allowable angle change.
295  double angleDelta = lastCharacterAngle - angle;
296  // normalise between -180 and 180
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 ) )
302  {
303  output->graphemePlacement.clear();
304  return output.release();
305  }
306  }
307 
308  // Shift the character downwards since the draw position is specified at the baseline
309  // and we're calculating the mean line here
310  double dist = 0.9 * metrics.characterHeight() / 2;
311  if ( output->flippedCharacterPlacementToGetUprightLabels )
312  {
313  dist = -dist;
314  }
315  characterStartX += dist * std::cos( angle + M_PI_2 );
316  characterStartY -= dist * std::sin( angle + M_PI_2 );
317 
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 )
326  {
327  // rotate in place
328  placement.x += characterWidth * std::cos( renderAngle );
329  placement.y -= characterWidth * std::sin( renderAngle );
330  renderAngle += M_PI;
331  }
332  placement.angle = -renderAngle;
333  output->graphemePlacement.push_back( placement );
334 
335  // Normalise to 0 <= angle < 2PI
336  while ( renderAngle >= 2 * M_PI )
337  renderAngle -= 2 * M_PI;
338  while ( renderAngle < 0 )
339  renderAngle += 2 * M_PI;
340 
341  if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
342  output->upsideDownCharCount++;
343  }
344 
345  if ( !isSecondAttempt && uprightOnly && output->upsideDownCharCount >= characterCount / 2.0 )
346  {
347  // more of text is upside down then right side up...
348  // if text should be shown upright then retry with the opposite orientation
349  return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly, true );
350  }
351 
352  return output.release();
353 }
354 
355 bool QgsTextRendererUtils::nextCharPosition( double charWidth, double segmentLength, const double *x, const double *y, int numPoints, int &index, double &currentDistanceAlongSegment, double &characterStartX, double &characterStartY, double &characterEndX, double &characterEndY )
356 {
357  // Coordinates this character will start at
358  if ( qgsDoubleNear( segmentLength, 0.0 ) )
359  {
360  // Not allowed to place across on 0 length segments or discontinuities
361  return false;
362  }
363 
364  double segmentStartX = x[index - 1];
365  double segmentStartY = y[index - 1];
366 
367  double segmentEndX = x[index];
368  double segmentEndY = y[index];
369 
370  const double segmentDx = segmentEndX - segmentStartX;
371  const double segmentDy = segmentEndY - segmentStartY;
372 
373  characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
374  characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
375 
376  // Coordinates this character ends at, calculated below
377  characterEndX = 0;
378  characterEndY = 0;
379 
380  if ( segmentLength - currentDistanceAlongSegment >= charWidth )
381  {
382  // if the distance remaining in this segment is enough, we just go further along the segment
383  currentDistanceAlongSegment += charWidth;
384  characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
385  characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
386  }
387  else
388  {
389  // If there isn't enough distance left on this segment
390  // then we need to search until we find the line segment that ends further than ci.width away
391  do
392  {
393  segmentStartX = segmentEndX;
394  segmentStartY = segmentEndY;
395  index++;
396  if ( index >= numPoints ) // Bail out if we run off the end of the shape
397  {
398  return false;
399  }
400  segmentEndX = x[index];
401  segmentEndY = y[index];
402  }
403  while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
404 
405  // Calculate the position to place the end of the character on
406  findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
407 
408  // Need to calculate distance on the new segment
409  currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
410  }
411  return true;
412 }
413 
414 void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
415 {
416  double multiplier = 1;
417  if ( radius < 10 )
418  {
419  // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
420  // CRS
421  multiplier = 10000;
422  x1 *= multiplier;
423  y1 *= multiplier;
424  x2 *= multiplier;
425  y2 *= multiplier;
426  cx *= multiplier;
427  cy *= multiplier;
428  radius *= multiplier;
429  }
430 
431  const double dx = x2 - x1;
432  const double dy = y2 - y1;
433 
434  const double A = dx * dx + dy * dy;
435  const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
436  const double C = ( x1 - cx ) * ( x1 - cx ) + ( y1 - cy ) * ( y1 - cy ) - radius * radius;
437 
438  const double det = B * B - 4 * A * C;
439  if ( A <= 0.000000000001 || det < 0 )
440  // Should never happen, No real solutions.
441  return;
442 
443  if ( qgsDoubleNear( det, 0.0 ) )
444  {
445  // Could potentially happen.... One solution.
446  const double t = -B / ( 2 * A );
447  xRes = x1 + t * dx;
448  yRes = y1 + t * dy;
449  }
450  else
451  {
452  // Two solutions.
453  // Always use the 1st one
454  // We only really have one solution here, as we know the line segment will start in the circle and end outside
455  const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
456  xRes = x1 + t * dx;
457  yRes = y1 + t * dy;
458  }
459 
460  if ( multiplier != 1 )
461  {
462  xRes /= multiplier;
463  yRes /= multiplier;
464  }
465 }
QgsTextRendererUtils::decodeBackgroundSizeType
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType(const QString &string)
Decodes a string representation of a background size type to a type.
Definition: qgstextrendererutils.cpp:48
QgsUnitTypes::RenderUnit
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
QgsPrecalculatedTextMetrics::characterHeight
double characterHeight() const
Character height (actually font metrics height, not individual character height).
Definition: qgstextmetrics.h:54
qgslinestring.h
QgsTextShadowSettings::ShadowBuffer
@ ShadowBuffer
Draw shadow under buffer.
Definition: qgstextshadowsettings.h:48
QgsTextRendererUtils::decodeBackgroundRotationType
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType(const QString &string)
Decodes a string representation of a background rotation type to a type.
Definition: qgstextrendererutils.cpp:61
QgsUnitTypes::RenderPoints
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
QgsTextRendererUtils::CurvePlacementProperties
Contains placement information for a curved text layout.
Definition: qgstextrendererutils.h:122
QgsUnitTypes::RenderPercentage
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:172
QgsTextBackgroundSettings::SizeType
SizeType
Methods for determining the background shape size.
Definition: qgstextbackgroundsettings.h:66
QgsLineString::yData
const double * yData() const
Returns a const pointer to the y vertex data.
Definition: qgslinestring.h:424
QgsUnitTypes::RenderMillimeters
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
QgsTextBackgroundSettings::RotationSync
@ RotationSync
Shape rotation is synced with text rotation.
Definition: qgstextbackgroundsettings.h:78
QgsTextRendererUtils::decodeTextOrientation
static QgsTextFormat::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
Definition: qgstextrendererutils.cpp:113
QgsTextRendererUtils::readColor
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.
Definition: qgstextrendererutils.cpp:146
QgsTextRendererUtils::encodeTextOrientation
static QString encodeTextOrientation(QgsTextFormat::TextOrientation orientation)
Encodes a text orientation.
Definition: qgstextrendererutils.cpp:99
QgsTextBackgroundSettings::RotationOffset
@ RotationOffset
Shape rotation is offset from text rotation.
Definition: qgstextbackgroundsettings.h:79
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsPrecalculatedTextMetrics::characterWidth
double characterWidth(int position) const
Returns the width of the character at the specified position.
Definition: qgstextmetrics.h:64
QgsLineString::xData
const double * xData() const
Returns a const pointer to the x vertex data.
Definition: qgslinestring.h:413
QgsTextBackgroundSettings::ShapeSquare
@ ShapeSquare
Square - buffered sizes only.
Definition: qgstextbackgroundsettings.h:56
QgsTextFormat::VerticalOrientation
@ VerticalOrientation
Horizontally oriented text.
Definition: qgstextformat.h:48
QgsTextRendererUtils::LabelLineDirection
LabelLineDirection
Controls behavior of curved text with respect to line directions.
Definition: qgstextrendererutils.h:139
QgsTextBackgroundSettings::RotationType
RotationType
Methods for determining the rotation of the background shape.
Definition: qgstextbackgroundsettings.h:76
qgstextrendererutils.h
QgsTextShadowSettings::ShadowPlacement
ShadowPlacement
Placement positions for text shadow.
Definition: qgstextshadowsettings.h:44
QgsTextBackgroundSettings::ShapeRectangle
@ ShapeRectangle
Rectangle.
Definition: qgstextbackgroundsettings.h:55
QgsPrecalculatedTextMetrics::count
int count() const
Returns the total number of characters.
Definition: qgstextmetrics.h:59
QgsTextRendererUtils::decodeShadowPlacementType
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType(const QString &string)
Decodes a string representation of a shadow placement type to a type.
Definition: qgstextrendererutils.cpp:78
QgsTextRendererUtils::decodeShapeType
static QgsTextBackgroundSettings::ShapeType decodeShapeType(const QString &string)
Decodes a string representation of a background shape type to a type.
Definition: qgstextrendererutils.cpp:20
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsTextBackgroundSettings::ShapeCircle
@ ShapeCircle
Circle.
Definition: qgstextbackgroundsettings.h:58
QgsTextShadowSettings::ShadowLowest
@ ShadowLowest
Draw shadow below all text components.
Definition: qgstextshadowsettings.h:46
QgsTextBackgroundSettings::SizeBuffer
@ SizeBuffer
Shape size is determined by adding a buffer margin around text.
Definition: qgstextbackgroundsettings.h:68
QgsTextShadowSettings::ShadowShape
@ ShadowShape
Draw shadow under background shape.
Definition: qgstextshadowsettings.h:49
QgsTextBackgroundSettings::ShapeEllipse
@ ShapeEllipse
Ellipse.
Definition: qgstextbackgroundsettings.h:57
qgsvectorlayer.h
QgsMapLayer::customProperty
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
Definition: qgsmaplayer.cpp:1999
QgsTextFormat::RotationBasedOrientation
@ RotationBasedOrientation
Horizontally or vertically oriented text based on rotation (only available for map labeling)
Definition: qgstextformat.h:49
QgsTextBackgroundSettings::ShapeSVG
@ ShapeSVG
SVG file.
Definition: qgstextbackgroundsettings.h:59
QgsTextBackgroundSettings::SizeFixed
@ SizeFixed
Fixed size.
Definition: qgstextbackgroundsettings.h:69
QgsTextRendererUtils::generateCurvedTextPlacement
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.
Definition: qgstextrendererutils.cpp:183
str
#define str(x)
Definition: qgis.cpp:37
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsTextShadowSettings::ShadowText
@ ShadowText
Draw shadow under text.
Definition: qgstextshadowsettings.h:47
QgsTextBackgroundSettings::ShapeType
ShapeType
Background shape types.
Definition: qgstextbackgroundsettings.h:53
QgsPrecalculatedTextMetrics
Contains precalculated properties regarding text metrics for text to be renderered at a later stage.
Definition: qgstextmetrics.h:34
QgsTextRendererUtils::RespectPainterOrientation
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
Definition: qgstextrendererutils.h:141
QgsTextBackgroundSettings::ShapeMarkerSymbol
@ ShapeMarkerSymbol
Marker symbol.
Definition: qgstextbackgroundsettings.h:60
QgsTextBackgroundSettings::RotationFixed
@ RotationFixed
Shape rotation is a fixed angle.
Definition: qgstextbackgroundsettings.h:80
QgsLineString::numPoints
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Definition: qgslinestring.cpp:986
QgsTextRendererUtils::convertFromOldLabelUnit
static QgsUnitTypes::RenderUnit convertFromOldLabelUnit(int val)
Converts a unit from an old (pre 3.0) label unit.
Definition: qgstextrendererutils.cpp:132
QgsTextFormat::HorizontalOrientation
@ HorizontalOrientation
Vertically oriented text.
Definition: qgstextformat.h:47
MathUtils::angle
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)
Definition: MathUtils.cpp:786
QgsUnitTypes::RenderMapUnits
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
QgsTextFormat::TextOrientation
TextOrientation
Text orientation.
Definition: qgstextformat.h:45