QGIS API Documentation 3.99.0-Master (a8882ad4560)
Loading...
Searching...
No Matches
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
17
19#include "qgsvectorlayer.h"
20
22{
24 const QString skind = string.trimmed();
25
26 if ( skind.compare( "Square"_L1, Qt::CaseInsensitive ) == 0 )
27 {
29 }
30 else if ( skind.compare( "Ellipse"_L1, Qt::CaseInsensitive ) == 0 )
31 {
33 }
34 else if ( skind.compare( "Circle"_L1, Qt::CaseInsensitive ) == 0 )
35 {
37 }
38 else if ( skind.compare( "SVG"_L1, Qt::CaseInsensitive ) == 0 )
39 {
41 }
42 else if ( skind.compare( "marker"_L1, Qt::CaseInsensitive ) == 0 )
43 {
45 }
46 return shpkind;
47}
48
50{
51 const QString stype = string.trimmed();
52 // "Buffer"
54
55 if ( stype.compare( "Fixed"_L1, Qt::CaseInsensitive ) == 0 )
56 {
58 }
59 return sizType;
60}
61
63{
64 const QString rotstr = string.trimmed();
65 // "Sync"
67
68 if ( rotstr.compare( "Offset"_L1, Qt::CaseInsensitive ) == 0 )
69 {
71 }
72 else if ( rotstr.compare( "Fixed"_L1, Qt::CaseInsensitive ) == 0 )
73 {
75 }
76 return rottype;
77}
78
80{
81 const QString str = string.trimmed();
82 // "Lowest"
84
85 if ( str.compare( "Text"_L1, Qt::CaseInsensitive ) == 0 )
86 {
88 }
89 else if ( str.compare( "Buffer"_L1, Qt::CaseInsensitive ) == 0 )
90 {
92 }
93 else if ( str.compare( "Background"_L1, Qt::CaseInsensitive ) == 0 )
94 {
96 }
97 return shdwtype;
98}
99
101{
102 switch ( orientation )
103 {
105 return u"horizontal"_s;
107 return u"vertical"_s;
109 return u"rotation-based"_s;
110 }
111 return QString();
112}
113
115{
116 if ( ok )
117 *ok = true;
118
119 const QString cleaned = name.toLower().trimmed();
120
121 if ( cleaned == "horizontal"_L1 )
123 else if ( cleaned == "vertical"_L1 )
125 else if ( cleaned == "rotation-based"_L1 )
127
128 if ( ok )
129 *ok = false;
131}
132
134{
135 if ( val == 0 )
137 else if ( val == 1 )
139 else if ( val == 2 )
141 else if ( val == 3 )
143 else
145}
146
147QColor QgsTextRendererUtils::readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor, bool withAlpha )
148{
149 const int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
150 const int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
151 const int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
152 const int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
153 return QColor( r, g, b, a );
154}
155
156std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement( const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction, double maxConcaveAngle, double maxConvexAngle, Qgis::CurvedTextFlags flags )
157{
158 const std::size_t numPoints = line.size();
159 std::vector<double> pathDistances( numPoints );
160
161 const QPointF *p = line.data();
162 double dx, dy;
163
164 pathDistances[0] = 0;
165 double prevX = p->x();
166 double prevY = p->y();
167 p++;
168
169 std::vector< double > x( numPoints );
170 std::vector< double > y( numPoints );
171 x[0] = prevX;
172 y[0] = prevY;
173
174 for ( std::size_t i = 1; i < numPoints; ++i )
175 {
176 dx = p->x() - prevX;
177 dy = p->y() - prevY;
178 pathDistances[i] = std::sqrt( dx * dx + dy * dy );
179
180 prevX = p->x();
181 prevY = p->y();
182 p++;
183 x[i] = prevX;
184 y[i] = prevY;
185 }
186
187 return generateCurvedTextPlacementPrivate( metrics, x.data(), y.data(), numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false );
188}
189
190std::unique_ptr< 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, Qgis::CurvedTextFlags flags, double additionalCharacterSpacing, double additionalWordSpacing )
191{
192 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, additionalCharacterSpacing, additionalWordSpacing );
193}
194
195std::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, Qgis::CurvedTextFlags flags, double maxConcaveAngle, double maxConvexAngle, bool isSecondAttempt, double additionalCharacterSpacing, double additionalWordSpacing )
196{
197 auto output = std::make_unique< CurvePlacementProperties >();
198 output->graphemePlacement.reserve( metrics.count() );
199
200 if ( !qgsDoubleNear( additionalCharacterSpacing, 0 ) || !qgsDoubleNear( additionalWordSpacing, 0 ) )
202
203 double offsetAlongSegment = offsetAlongLine;
204 int index = 1;
205 // Find index of segment corresponding to starting offset
206 while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
207 {
208 offsetAlongSegment -= pathDistances[index];
209 index += 1;
210 }
211 if ( index >= numPoints )
212 {
213 return output;
214 }
215
216 const double segmentLength = pathDistances[index];
217 if ( qgsDoubleNear( segmentLength, 0.0 ) )
218 {
219 // Not allowed to place across on 0 length segments or discontinuities
220 return output;
221 }
222
223 int characterCount = metrics.count();
224
225 if ( direction == RespectPainterOrientation && !isSecondAttempt )
226 {
227 // Calculate the orientation based on the angle of the path segment under consideration
228
229 double distance = offsetAlongSegment;
230 int endindex = index;
231
232 double startLabelX = 0;
233 double startLabelY = 0;
234 double endLabelX = 0;
235 double endLabelY = 0;
236 for ( int i = 0; i < characterCount; i++ )
237 {
238 const double characterWidth = metrics.characterWidth( i );
239 double characterStartX, characterStartY;
240
241 // calculate additional spacing for this character
242 double currentSpacing = 0.0;
243 if ( i > 0 )
244 {
245 currentSpacing = additionalCharacterSpacing;
246 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
247 {
248 const QString g = metrics.grapheme( i - 1 );
249 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
250 {
251 currentSpacing += additionalWordSpacing;
252 }
253 }
254 }
255
256 if ( !nextCharPosition( characterWidth, pathDistances, x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY, flags, currentSpacing ) )
257 {
259 {
260 characterCount = i + 1;
261 break;
262 }
263 else
264 {
265 return output;
266 }
267 }
268 if ( i == 0 )
269 {
270 startLabelX = characterStartX;
271 startLabelY = characterStartY;
272 }
273 }
274
275 // Determine the angle of the path segment under consideration
276 const double dx = endLabelX - startLabelX;
277 const double dy = endLabelY - startLabelY;
278 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
279
280 if ( lineAngle > 90 || lineAngle < -90 )
281 {
282 output->labeledLineSegmentIsRightToLeft = true;
283 }
284 }
285
286 if ( isSecondAttempt )
287 {
288 // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
289 // segment as left to right
290 output->labeledLineSegmentIsRightToLeft = false;
291 output->flippedCharacterPlacementToGetUprightLabels = true;
292 }
293
294 const double dx = x[index] - x[index - 1];
295 const double dy = y[index] - y[index - 1];
296
297 double angle = std::atan2( -dy, dx );
298
299 const double maxCharacterDescent = metrics.maximumCharacterDescent();
300 const double maxCharacterHeight = metrics.maximumCharacterHeight();
301
302 for ( int i = 0; i < characterCount; i++ )
303 {
304 const double lastCharacterAngle = angle;
305
306 // next character index, depending on the orientation
307 const int k = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
308
309 // grab the next character according to the orientation
310 const double characterWidth = metrics.characterWidth( k );
311 if ( qgsDoubleNear( characterWidth, 0.0 ) )
312 // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
313 continue;
314
315 const double characterHeight = metrics.characterHeight( k );
316 const double characterDescent = metrics.characterDescent( k );
317
318 double characterStartX = 0;
319 double characterStartY = 0;
320 double characterEndX = 0;
321 double characterEndY = 0;
322
323 // Calculate Spacing
324 double currentSpacing = 0.0;
325 if ( i > 0 )
326 {
327 currentSpacing = additionalCharacterSpacing;
328 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
329 {
330 int prevCharIndex = !output->flippedCharacterPlacementToGetUprightLabels ? k - 1 : k + 1;
331 if ( prevCharIndex >= 0 && prevCharIndex < metrics.count() )
332 {
333 const QString g = metrics.grapheme( prevCharIndex );
334 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
335 currentSpacing += additionalWordSpacing;
336 }
337 }
338 }
339
340 if ( !nextCharPosition( characterWidth, pathDistances, x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY, flags, currentSpacing ) )
341 {
343 {
344 characterCount = i + 1;
345 break;
346 }
347 else
348 {
349 output->graphemePlacement.clear();
350 return output;
351 }
352 }
353
354 // Calculate angle from the start of the character to the end based on start/end of character
355 angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
356
357 if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
358 {
359 // Test lastCharacterAngle vs angle
360 // since our rendering angle has changed then check against our
361 // max allowable angle change.
362 double angleDelta = lastCharacterAngle - angle;
363 // normalise between -180 and 180
364 while ( angleDelta > M_PI )
365 angleDelta -= 2 * M_PI;
366 while ( angleDelta < -M_PI )
367 angleDelta += 2 * M_PI;
368 if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
369 {
370 output->graphemePlacement.clear();
371 return output;
372 }
373 }
374
376 {
377 // Shift the character downwards since the draw position is specified at the baseline
378 // and we're calculating the mean line here
379 double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
380 if ( output->flippedCharacterPlacementToGetUprightLabels )
381 {
382 dist = -dist;
383 }
384 characterStartX += dist * std::cos( angle + M_PI_2 );
385 characterStartY -= dist * std::sin( angle + M_PI_2 );
386 }
387
388 double renderAngle = angle;
389 CurvedGraphemePlacement placement;
390 placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
391 placement.x = characterStartX;
392 placement.y = characterStartY;
393 placement.width = characterWidth;
394 placement.height = characterHeight;
395 const QString grapheme = metrics.grapheme( placement.graphemeIndex );
396 placement.isWhitespace = grapheme.isEmpty() || grapheme.at( 0 ).isSpace() || grapheme.at( 0 ) == '\t';
397 if ( output->flippedCharacterPlacementToGetUprightLabels )
398 {
399 // rotate in place
400 placement.x += characterWidth * std::cos( renderAngle );
401 placement.y -= characterWidth * std::sin( renderAngle );
402 renderAngle += M_PI;
403 }
404 placement.angle = -renderAngle;
405 output->graphemePlacement.push_back( placement );
406
407 // Normalise to 0 <= angle < 2PI
408 while ( renderAngle >= 2 * M_PI )
409 renderAngle -= 2 * M_PI;
410 while ( renderAngle < 0 )
411 renderAngle += 2 * M_PI;
412
413 if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
414 output->upsideDownCharCount++;
415 }
416
417 if ( !isSecondAttempt && ( flags & Qgis::CurvedTextFlag::UprightCharactersOnly ) && output->upsideDownCharCount >= characterCount / 2.0 )
418 {
419 // more of text is upside down then right side up...
420 // if text should be shown upright then retry with the opposite orientation
421 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, true, additionalCharacterSpacing, additionalWordSpacing );
422 }
423
424 return output;
425}
426
427bool QgsTextRendererUtils::nextCharPosition( double charWidth, const std::vector<double> &pathDistances, const double *x, const double *y, int numPoints, int &index, double &currentDistanceAlongSegment, double &characterStartX, double &characterStartY, double &characterEndX, double &characterEndY, Qgis::CurvedTextFlags flags, double additionalSpacing )
428{
429 if ( !qgsDoubleNear( additionalSpacing, 0.0 ) )
430 {
431 currentDistanceAlongSegment += additionalSpacing;
432
433 // forward spacing
434 while ( index < numPoints && currentDistanceAlongSegment > pathDistances[index] )
435 {
436 currentDistanceAlongSegment -= pathDistances[index];
437 index++;
438 }
439 // backward spacing (compression)
440 while ( currentDistanceAlongSegment < 0 )
441 {
442 index--;
443 if ( index < 1 )
444 return false;
445 currentDistanceAlongSegment += pathDistances[index];
446 }
447 }
448
449 // intentional for readability:
450 // NOLINTBEGIN(bugprone-branch-clone)
451 if ( index >= numPoints )
452 {
453 // do not support extending the line start or end points via additional spacing
454 return false;
455 }
456 else if ( qgsDoubleNear( pathDistances[index], 0.0 ) )
457 {
458 // Not allowed to place across on 0 length segments or discontinuities
459 return false;
460 }
461 // NOLINTEND(bugprone-branch-clone)
462
463 double segmentStartX = x[index - 1];
464 double segmentStartY = y[index - 1];
465
466 double segmentEndX = x[index];
467 double segmentEndY = y[index];
468
469 double segmentLength = pathDistances[index];
470
471 const double segmentDx = segmentEndX - segmentStartX;
472 const double segmentDy = segmentEndY - segmentStartY;
473
474 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
475 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
476
477 // Coordinates this character ends at, calculated below
478 characterEndX = 0;
479 characterEndY = 0;
480
481 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
482 {
483 // if the distance remaining in this segment is enough, we just go further along the segment
484 currentDistanceAlongSegment += charWidth;
485 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
486 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
487 }
488 else
489 {
490 // If there isn't enough distance left on this segment
491 // then we need to search until we find the line segment that ends further than ci.width away
492 do
493 {
494 index++;
495 if ( index >= numPoints ) // Bail out if we run off the end of the shape
496 {
498 {
499 // here we should extend out the final segment of the line to fit the character
500 const double lastSegmentDx = segmentEndX - segmentStartX;
501 const double lastSegmentDy = segmentEndY - segmentStartY;
502 const double lastSegmentLength = std::sqrt( lastSegmentDx * lastSegmentDx + lastSegmentDy * lastSegmentDy );
503 if ( qgsDoubleNear( lastSegmentLength, 0.0 ) )
504 {
505 // last segment has 0 length, can't extend
506 return false;
507 }
508
509 segmentEndX = segmentStartX + ( lastSegmentDx / lastSegmentLength ) * charWidth;
510 segmentEndY = segmentStartY + ( lastSegmentDy / lastSegmentLength ) * charWidth;
511 index--;
512 break;
513
514 }
515 else
516 {
517 return false;
518 }
519 }
520
521 segmentStartX = segmentEndX;
522 segmentStartY = segmentEndY;
523 segmentEndX = x[index];
524 segmentEndY = y[index];
525 }
526 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
527
528 // Calculate the position to place the end of the character on
529 findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
530
531 // Need to calculate distance on the new segment
532 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
533 }
534 return true;
535}
536
537void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
538{
539 double multiplier = 1;
540 if ( radius < 10 )
541 {
542 // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
543 // CRS
544 multiplier = 10000;
545 x1 *= multiplier;
546 y1 *= multiplier;
547 x2 *= multiplier;
548 y2 *= multiplier;
549 cx *= multiplier;
550 cy *= multiplier;
551 radius *= multiplier;
552 }
553
554 const double dx = x2 - x1;
555 const double dy = y2 - y1;
556
557 const double A = dx * dx + dy * dy;
558 const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
559 const double C = QgsGeometryUtilsBase::sqrDistance2D( x1, y1, cx, cy ) - radius * radius;
560
561 const double det = B * B - 4 * A * C;
562 if ( A <= 0.000000000001 || det < 0 )
563 // Should never happen, No real solutions.
564 return;
565
566 if ( qgsDoubleNear( det, 0.0 ) )
567 {
568 // Could potentially happen.... One solution.
569 const double t = -B / ( 2 * A );
570 xRes = x1 + t * dx;
571 yRes = y1 + t * dy;
572 }
573 else
574 {
575 // Two solutions.
576 // Always use the 1st one
577 // We only really have one solution here, as we know the line segment will start in the circle and end outside
578 const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
579 xRes = x1 + t * dx;
580 yRes = y1 + t * dy;
581 }
582
583 if ( multiplier != 1 )
584 {
585 xRes /= multiplier;
586 yRes /= multiplier;
587 }
588}
TextOrientation
Text orientations.
Definition qgis.h:2932
@ Vertical
Vertically oriented text.
Definition qgis.h:2934
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling).
Definition qgis.h:2935
@ Horizontal
Horizontally oriented text.
Definition qgis.h:2933
RenderUnit
Rendering size units.
Definition qgis.h:5243
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5247
@ Millimeters
Millimeters.
Definition qgis.h:5244
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5248
@ MapUnits
Map units.
Definition qgis.h:5245
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
Definition qgis.h:3037
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
Definition qgis.h:3039
@ ExtendLineToFitText
When a string is too long for the line, extend the line's final segment to fit the entire string.
Definition qgis.h:3040
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
Definition qgis.h:3038
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
Definition qgis.h:3049
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 rendered 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.
QString grapheme(int index) const
Returns the grapheme at the specified index.
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.
@ ShapeSquare
Square - buffered sizes only.
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 single grapheme in a curved text layout.
int graphemeIndex
Index of corresponding grapheme.
static QgsTextBackgroundSettings::ShapeType decodeShapeType(const QString &string)
Decodes a string representation of a background shape type to a type.
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, Qgis::CurvedTextFlags flags=Qgis::CurvedTextFlags())
Calculates curved text placement properties.
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.
static Qgis::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.
Represents a vector layer which manages a vector based dataset.
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).
Definition qgis.h:6888