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;
134 return Qgis::RenderUnit::Points;
136 return Qgis::RenderUnit::Millimeters;
138 return Qgis::RenderUnit::MapUnits;
140 return Qgis::RenderUnit::Percentage;
142 return Qgis::RenderUnit::Millimeters;
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.
RenderUnit
Rendering size units.
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 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 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)