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 );
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 const double dx = endLabelX - startLabelX;
245 const 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 const double dx = x[index] - x[index - 1];
263 const double dy = y[index] - y[index - 1];
265 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 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 const double segmentDx = segmentEndX - segmentStartX;
371 const 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;
431 const double dx = x2 - x1;
432 const double dy = y2 - y1;
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;
438 const double det = B * B - 4 * A * C;
439 if ( A <= 0.000000000001 || det < 0 )
446 const double t = -B / ( 2 * A );
455 const 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)