QGIS API Documentation 3.41.0-Master (cea29feecf2)
Loading...
Searching...
No Matches
qgslinearreferencingsymbollayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslinearreferencingsymbollayer.cpp
3 ---------------------
4 begin : August 2024
5 copyright : (C) 2024 by 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#include "qgsrendercontext.h"
18#include "qgstextrenderer.h"
19#include "qgslinestring.h"
20#include "qgspolygon.h"
21#include "qgsmarkersymbol.h"
23#include "qgsapplication.h"
25#include "qgsgeometryutils.h"
26#include "qgsunittypes.h"
27#include "qgssymbollayerutils.h"
28#include "qgstextlabelfeature.h"
29#include "qgsgeos.h"
30#include "qgspallabeling.h"
31#include "labelposition.h"
32#include "feature.h"
33
35class QgsTextLabelFeatureWithFormat : public QgsTextLabelFeature
36{
37 public:
38 QgsTextLabelFeatureWithFormat( QgsFeatureId id, geos::unique_ptr geometry, QSizeF size, const QgsTextFormat &format )
39 : QgsTextLabelFeature( id, std::move( geometry ), size )
40 , mFormat( format )
41 {}
42
43 QgsTextFormat mFormat;
44
45};
46
47class QgsLinearReferencingSymbolLayerLabelProvider final : public QgsAbstractLabelProvider
48{
49 public:
50 QgsLinearReferencingSymbolLayerLabelProvider()
51 : QgsAbstractLabelProvider( nullptr )
52 {
54 mFlags |= DrawLabels;
55
56 // always consider these labels highest priority
57 // TODO possibly expose as a setting for the symbol layer?
58 mPriority = 0;
59 }
60
61 ~QgsLinearReferencingSymbolLayerLabelProvider()
62 {
63 qDeleteAll( mLabels );
64 }
65
66 void addLabel( const QPointF &painterPoint, double angleRadians, const QString &text, QgsRenderContext &context, const QgsTextFormat &format )
67 {
68 // labels must be registered in destination map units
69 QgsPoint mapPoint( painterPoint );
70 mapPoint.transform( context.mapToPixel().transform().inverted() );
71
72 const QgsTextDocument doc = QgsTextDocument::fromTextAndFormat( { text }, format );
73 QgsTextDocumentMetrics documentMetrics = QgsTextDocumentMetrics::calculateMetrics( doc, format, context );
74 const QSizeF size = documentMetrics.documentSize( Qgis::TextLayoutMode::Point, Qgis::TextOrientation::Horizontal );
75
76 double uPP = context.mapToPixel().mapUnitsPerPixel();
77 std::unique_ptr< QgsTextLabelFeatureWithFormat > feature = std::make_unique< QgsTextLabelFeatureWithFormat >( mLabels.size(),
78 QgsGeos::asGeos( &mapPoint ), QSizeF( size.width() * uPP, size.height() * uPP ), format );
79
80 feature->setDocument( doc, documentMetrics );
81 feature->setFixedAngle( angleRadians );
82 feature->setHasFixedAngle( true );
83 // above right
84 // TODO: we could potentially expose this, and use a non-fixed mode to allow fallback positions
85 feature->setQuadOffset( QPointF( 1, 1 ) );
86
87 mLabels.append( feature.release() );
88 }
89
90 QList<QgsLabelFeature *> labelFeatures( QgsRenderContext & ) final
91 {
92 return mLabels;
93 }
94
95 void drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const final
96 {
97 // as per vector label rendering...
98 QgsMapToPixel xform = context.mapToPixel();
99 xform.setMapRotation( 0, 0, 0 );
100 const QPointF outPt = context.mapToPixel().transform( label->getX(), label->getY() ).toQPointF();
101
102 QgsTextLabelFeatureWithFormat *lf = qgis::down_cast<QgsTextLabelFeatureWithFormat *>( label->getFeaturePart()->feature() );
104 lf->mFormat, lf->document(), lf->documentMetrics(), context, Qgis::TextHorizontalAlignment::Left,
105 label->getAlpha() );
106 }
107
108 private:
109 QList<QgsLabelFeature *> mLabels;
110
111};
113
116{
117 mNumericFormat = std::make_unique< QgsBasicNumericFormat >();
118}
119
121
123{
124 std::unique_ptr< QgsLinearReferencingSymbolLayer > res = std::make_unique< QgsLinearReferencingSymbolLayer >();
125 res->setPlacement( qgsEnumKeyToValue( properties.value( QStringLiteral( "placement" ) ).toString(), Qgis::LinearReferencingPlacement::IntervalCartesian2D ) );
126 res->setLabelSource( qgsEnumKeyToValue( properties.value( QStringLiteral( "source" ) ).toString(), Qgis::LinearReferencingLabelSource::CartesianDistance2D ) );
127 bool ok = false;
128 const double interval = properties.value( QStringLiteral( "interval" ) ).toDouble( &ok );
129 if ( ok )
130 res->setInterval( interval );
131 const double skipMultiples = properties.value( QStringLiteral( "skip_multiples" ) ).toDouble( &ok );
132 if ( ok )
133 res->setSkipMultiplesOf( skipMultiples );
134 res->setRotateLabels( properties.value( QStringLiteral( "rotate" ), true ).toBool() );
135 res->setShowMarker( properties.value( QStringLiteral( "show_marker" ), false ).toBool() );
136
137 // it's impossible to get the project's path resolver here :(
138 // TODO QGIS 4.0 -- force use of QgsReadWriteContext in create methods
139 QgsReadWriteContext rwContext;
140 //rwContext.setPathResolver( QgsProject::instance()->pathResolver() ); // skip-keyword-check
141
142 const QString textFormatXml = properties.value( QStringLiteral( "text_format" ) ).toString();
143 if ( !textFormatXml.isEmpty() )
144 {
145 QDomDocument doc;
146 QDomElement elem;
147 doc.setContent( textFormatXml );
148 elem = doc.documentElement();
149
151 textFormat.readXml( elem, rwContext );
152 res->setTextFormat( textFormat );
153 }
154
155 const QString numericFormatXml = properties.value( QStringLiteral( "numeric_format" ) ).toString();
156 if ( !numericFormatXml.isEmpty() )
157 {
158 QDomDocument doc;
159 doc.setContent( numericFormatXml );
160 res->setNumericFormat( QgsApplication::numericFormatRegistry()->createFromXml( doc.documentElement(), rwContext ) );
161 }
162
163 if ( properties.contains( QStringLiteral( "label_offset" ) ) )
164 {
165 res->setLabelOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "label_offset" )].toString() ) );
166 }
167 if ( properties.contains( QStringLiteral( "label_offset_unit" ) ) )
168 {
169 res->setLabelOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "label_offset_unit" )].toString() ) );
170 }
171 if ( properties.contains( ( QStringLiteral( "label_offset_map_unit_scale" ) ) ) )
172 {
173 res->setLabelOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "label_offset_map_unit_scale" )].toString() ) );
174 }
175 if ( properties.contains( QStringLiteral( "average_angle_length" ) ) )
176 {
177 res->setAverageAngleLength( properties[QStringLiteral( "average_angle_length" )].toDouble() );
178 }
179 if ( properties.contains( QStringLiteral( "average_angle_unit" ) ) )
180 {
181 res->setAverageAngleUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "average_angle_unit" )].toString() ) );
182 }
183 if ( properties.contains( ( QStringLiteral( "average_angle_map_unit_scale" ) ) ) )
184 {
185 res->setAverageAngleMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "average_angle_map_unit_scale" )].toString() ) );
186 }
187
188 return res.release();
189}
190
192{
193 std::unique_ptr< QgsLinearReferencingSymbolLayer > res = std::make_unique< QgsLinearReferencingSymbolLayer >();
194 res->setPlacement( mPlacement );
195 res->setLabelSource( mLabelSource );
196 res->setInterval( mInterval );
197 res->setSkipMultiplesOf( mSkipMultiplesOf );
198 res->setRotateLabels( mRotateLabels );
199 res->setLabelOffset( mLabelOffset );
200 res->setLabelOffsetUnit( mLabelOffsetUnit );
201 res->setLabelOffsetMapUnitScale( mLabelOffsetMapUnitScale );
202 res->setShowMarker( mShowMarker );
203 res->setAverageAngleLength( mAverageAngleLength );
204 res->setAverageAngleUnit( mAverageAngleLengthUnit );
205 res->setAverageAngleMapUnitScale( mAverageAngleLengthMapUnitScale );
206
207 res->mTextFormat = mTextFormat;
208 res->mMarkerSymbol.reset( mMarkerSymbol ? mMarkerSymbol->clone() : nullptr );
209 if ( mNumericFormat )
210 res->mNumericFormat.reset( mNumericFormat->clone() );
211
212 copyDataDefinedProperties( res.get() );
213 copyPaintEffect( res.get() );
214
215 return res.release();
216}
217
219{
220 QDomDocument textFormatDoc;
221 // it's impossible to get the project's path resolver here :(
222 // TODO QGIS 4.0 -- force use of QgsReadWriteContext in properties methods
223 QgsReadWriteContext rwContext;
224 // rwContext.setPathResolver( QgsProject::instance()->pathResolver() ); // skip-keyword-check
225 const QDomElement textElem = mTextFormat.writeXml( textFormatDoc, rwContext );
226 textFormatDoc.appendChild( textElem );
227
228 QDomDocument numericFormatDoc;
229 QDomElement numericFormatElem = numericFormatDoc.createElement( QStringLiteral( "numericFormat" ) );
230 mNumericFormat->writeXml( numericFormatElem, numericFormatDoc, rwContext );
231 numericFormatDoc.appendChild( numericFormatElem );
232
233 QVariantMap res
234 {
235 {
236 QStringLiteral( "placement" ), qgsEnumValueToKey( mPlacement )
237 },
238 {
239 QStringLiteral( "source" ), qgsEnumValueToKey( mLabelSource )
240 },
241 {
242 QStringLiteral( "interval" ), mInterval
243 },
244 {
245 QStringLiteral( "rotate" ), mRotateLabels
246 },
247 {
248 QStringLiteral( "show_marker" ), mShowMarker
249 },
250 {
251 QStringLiteral( "text_format" ), textFormatDoc.toString()
252 },
253 {
254 QStringLiteral( "numeric_format" ), numericFormatDoc.toString()
255 },
256 {
257 QStringLiteral( "label_offset" ), QgsSymbolLayerUtils::encodePoint( mLabelOffset )
258 },
259 {
260 QStringLiteral( "label_offset_unit" ), QgsUnitTypes::encodeUnit( mLabelOffsetUnit )
261 },
262 {
263 QStringLiteral( "label_offset_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mLabelOffsetMapUnitScale )
264 },
265 {
266 QStringLiteral( "average_angle_length" ), mAverageAngleLength
267 },
268 {
269 QStringLiteral( "average_angle_unit" ), QgsUnitTypes::encodeUnit( mAverageAngleLengthUnit )
270 },
271 {
272 QStringLiteral( "average_angle_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mAverageAngleLengthMapUnitScale )
273 },
274 };
275
276 if ( mSkipMultiplesOf >= 0 )
277 {
278 res.insert( QStringLiteral( "skip_multiples" ), mSkipMultiplesOf );
279 }
280
281 return res;
282}
283
285{
286 return QStringLiteral( "LinearReferencing" );
287}
288
294
296{
297 return mShowMarker ? mMarkerSymbol.get() : nullptr;
298}
299
301{
302 if ( symbol && symbol->type() == Qgis::SymbolType::Marker )
303 {
304 mMarkerSymbol.reset( qgis::down_cast<QgsMarkerSymbol *>( symbol ) );
305 return true;
306 }
307 delete symbol;
308 return false;
309}
310
312{
313 if ( mMarkerSymbol )
314 {
315 Qgis::SymbolRenderHints hints = mMarkerSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol;
316 if ( mRotateLabels )
318 mMarkerSymbol->setRenderHints( hints );
319
320 mMarkerSymbol->startRender( context.renderContext(), context.fields() );
321 }
322
323 if ( QgsLabelingEngine *labelingEngine = context.renderContext().labelingEngine() )
324 {
325 // rendering with a labeling engine. In this scenario we will register rendered text as labels, so that they participate in the labeling problem
326 // for the map
327 QgsLinearReferencingSymbolLayerLabelProvider *provider = new QgsLinearReferencingSymbolLayerLabelProvider();
328 mLabelProviderId = labelingEngine->addProvider( provider );
329 }
330}
331
333{
334 if ( mMarkerSymbol )
335 {
336 mMarkerSymbol->stopRender( context.renderContext() );
337 }
338}
339
340void QgsLinearReferencingSymbolLayer::renderGeometryPart( QgsSymbolRenderContext &context, const QgsAbstractGeometry *geometry, double labelOffsetPainterUnitsX, double labelOffsetPainterUnitsY, double skipMultiples, double averageAngleDistancePainterUnits, bool showMarker )
341{
342 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( geometry ) )
343 {
344 renderLineString( context, line, labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
345 }
346 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( geometry ) )
347 {
348 renderLineString( context, qgsgeometry_cast< const QgsLineString *>( polygon->exteriorRing() ), labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
349 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
350 {
351 renderLineString( context, qgsgeometry_cast< const QgsLineString *>( polygon->interiorRing( i ) ), labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
352 }
353 }
354}
355
356void QgsLinearReferencingSymbolLayer::renderLineString( QgsSymbolRenderContext &context, const QgsLineString *line, double labelOffsetPainterUnitsX, double labelOffsetPainterUnitsY, double skipMultiples, double averageAngleDistancePainterUnits, bool showMarker )
357{
358 if ( !line )
359 return;
360
361 switch ( mPlacement )
362 {
366 renderPolylineInterval( line, context, skipMultiples, QPointF( labelOffsetPainterUnitsX, labelOffsetPainterUnitsY ), averageAngleDistancePainterUnits, showMarker );
367 break;
368
370 renderPolylineVertex( line, context, skipMultiples, QPointF( labelOffsetPainterUnitsX, labelOffsetPainterUnitsY ), averageAngleDistancePainterUnits, showMarker );
371 break;
372 }
373}
374
376{
377 QPainter *p = context.renderContext().painter();
378 if ( !p )
379 {
380 return;
381 }
382
383 // TODO (maybe?): if we don't have an original geometry, convert points to linestring and scale distance to painter units?
384 // in reality this line type makes no sense for rendering non-real feature geometries...
385 ( void )points;
386 const QgsAbstractGeometry *geometry = context.renderContext().geometry();
387 if ( !geometry )
388 return;
389
390 double skipMultiples = mSkipMultiplesOf;
392 {
393 context.setOriginalValueVariable( mSkipMultiplesOf );
395 }
396
397 double labelOffsetX = mLabelOffset.x();
398 double labelOffsetY = mLabelOffset.y();
399
400 double averageOver = mAverageAngleLength;
402 {
403 context.setOriginalValueVariable( mAverageAngleLength );
405 }
406
407 bool showMarker = mShowMarker;
409 {
412 }
413
414 const double labelOffsetPainterUnitsX = context.renderContext().convertToPainterUnits( labelOffsetX, mLabelOffsetUnit, mLabelOffsetMapUnitScale );
415 const double labelOffsetPainterUnitsY = context.renderContext().convertToPainterUnits( labelOffsetY, mLabelOffsetUnit, mLabelOffsetMapUnitScale );
416 const double averageAngleDistancePainterUnits = context.renderContext().convertToPainterUnits( averageOver, mAverageAngleLengthUnit, mAverageAngleLengthMapUnitScale ) / 2;
417
418 for ( auto partIt = geometry->const_parts_begin(); partIt != geometry->const_parts_end(); ++partIt )
419 {
420 renderGeometryPart( context, *partIt, labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
421 }
422}
423
424
425double calculateAveragedAngle( double targetPointDistanceAlongSegment, double segmentLengthPainterUnits,
426 double averageAngleLengthPainterUnits, double prevXPainterUnits, double prevYPainterUnits,
427 double thisXPainterUnits, double thisYPainterUnits, const double *xPainterUnits,
428 const double *yPainterUnits, int totalPoints, int i )
429{
430
431 // track forward by averageAngleLengthPainterUnits
432 double painterDistRemaining = averageAngleLengthPainterUnits + targetPointDistanceAlongSegment;
433 double startAverageSegmentX = prevXPainterUnits;
434 double startAverageSegmentY = prevYPainterUnits;
435 double endAverageSegmentX = thisXPainterUnits;
436 double endAverageSegmentY = thisYPainterUnits;
437 double averagingSegmentLengthPainterUnits = segmentLengthPainterUnits;
438 const double *xAveragingData = xPainterUnits;
439 const double *yAveragingData = yPainterUnits;
440
441 int j = i;
442 while ( painterDistRemaining > averagingSegmentLengthPainterUnits )
443 {
444 if ( j >= totalPoints - 1 )
445 break;
446
447 painterDistRemaining -= averagingSegmentLengthPainterUnits;
448 startAverageSegmentX = endAverageSegmentX;
449 startAverageSegmentY = endAverageSegmentY;
450
451 endAverageSegmentX = *xAveragingData++;
452 endAverageSegmentY = *yAveragingData++;
453 j++;
454 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
455 }
456 // fits on this same segment
457 double endAverageXPainterUnits;
458 double endAverageYPainterUnits;
459 if ( painterDistRemaining < averagingSegmentLengthPainterUnits )
460 {
461 QgsGeometryUtilsBase::pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, endAverageXPainterUnits, endAverageYPainterUnits,
462 nullptr, nullptr, nullptr,
463 nullptr, nullptr, nullptr );
464 }
465 else
466 {
467 endAverageXPainterUnits = endAverageSegmentX;
468 endAverageYPainterUnits = endAverageSegmentY;
469 }
470
471 // also track back by averageAngleLengthPainterUnits
472 j = i;
473 painterDistRemaining = ( segmentLengthPainterUnits - targetPointDistanceAlongSegment ) + averageAngleLengthPainterUnits;
474 startAverageSegmentX = thisXPainterUnits;
475 startAverageSegmentY = thisYPainterUnits;
476 endAverageSegmentX = prevXPainterUnits;
477 endAverageSegmentY = prevYPainterUnits;
478 averagingSegmentLengthPainterUnits = segmentLengthPainterUnits;
479 xAveragingData = xPainterUnits - 2;
480 yAveragingData = yPainterUnits - 2;
481 while ( painterDistRemaining > averagingSegmentLengthPainterUnits )
482 {
483 if ( j < 1 )
484 break;
485
486 painterDistRemaining -= averagingSegmentLengthPainterUnits;
487 startAverageSegmentX = endAverageSegmentX;
488 startAverageSegmentY = endAverageSegmentY;
489
490 endAverageSegmentX = *xAveragingData--;
491 endAverageSegmentY = *yAveragingData--;
492 j--;
493 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
494 }
495 // fits on this same segment
496 double startAverageXPainterUnits;
497 double startAverageYPainterUnits;
498 if ( painterDistRemaining < averagingSegmentLengthPainterUnits )
499 {
500 QgsGeometryUtilsBase::pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, startAverageXPainterUnits, startAverageYPainterUnits,
501 nullptr, nullptr, nullptr,
502 nullptr, nullptr, nullptr );
503 }
504 else
505 {
506 startAverageXPainterUnits = endAverageSegmentX;
507 startAverageYPainterUnits = endAverageSegmentY;
508 }
509
510 double calculatedAngle = std::fmod( QgsGeometryUtilsBase::azimuth( startAverageXPainterUnits, startAverageYPainterUnits, endAverageXPainterUnits, endAverageYPainterUnits ) + 360, 360 );
511 if ( calculatedAngle > 90 && calculatedAngle < 270 )
512 calculatedAngle += 180;
513
514 return calculatedAngle;
515}
516
517typedef std::function<bool ( double x, double y, double z, double m, double distanceFromStart, double angle )> VisitPointFunction;
518typedef std::function< void( const QgsLineString *, const QgsLineString *, bool, double, double, const VisitPointFunction & ) > VisitPointAtDistanceFunction;
519
520void visitPointsByRegularDistance( const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint )
521{
522 if ( distance < 0 )
523 return;
524
525 double distanceTraversed = 0;
526 const int totalPoints = line->numPoints();
527 if ( totalPoints == 0 )
528 return;
529
530 const double *x = line->xData();
531 const double *y = line->yData();
532 const double *z = line->is3D() ? line->zData() : nullptr;
533 const double *m = line->isMeasure() ? line->mData() : nullptr;
534
535 const double *xPainterUnits = linePainterUnits->xData();
536 const double *yPainterUnits = linePainterUnits->yData();
537
538 double prevX = *x++;
539 double prevY = *y++;
540 double prevZ = z ? *z++ : 0.0;
541 double prevM = m ? *m++ : 0.0;
542
543 double prevXPainterUnits = *xPainterUnits++;
544 double prevYPainterUnits = *yPainterUnits++;
545
546 if ( qgsDoubleNear( distance, 0.0 ) )
547 {
548 visitPoint( prevX, prevY, prevZ, prevM, 0, 0 );
549 return;
550 }
551
552 double pZ = std::numeric_limits<double>::quiet_NaN();
553 double pM = std::numeric_limits<double>::quiet_NaN();
554 double nextPointDistance = emitFirstPoint ? 0 : distance;
555 for ( int i = 1; i < totalPoints; ++i )
556 {
557 double thisX = *x++;
558 double thisY = *y++;
559 double thisZ = z ? *z++ : 0.0;
560 double thisM = m ? *m++ : 0.0;
561 double thisXPainterUnits = *xPainterUnits++;
562 double thisYPainterUnits = *yPainterUnits++;
563
564 double angle = std::fmod( QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 );
565 if ( angle > 90 && angle < 270 )
566 angle += 180;
567
568 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
569 const double segmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( thisXPainterUnits, thisYPainterUnits, prevXPainterUnits, prevYPainterUnits );
570
571 while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
572 {
573 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
574 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
575 double pX, pY;
576 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
577 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
578 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
579
580 double calculatedAngle = angle;
581 if ( averageAngleLengthPainterUnits > 0 )
582 {
583 const double targetPointFractionAlongSegment = distanceToPoint / segmentLength;
584 const double targetPointDistanceAlongSegment = targetPointFractionAlongSegment * segmentLengthPainterUnits;
585
586 calculatedAngle = calculateAveragedAngle( targetPointDistanceAlongSegment, segmentLengthPainterUnits,
587 averageAngleLengthPainterUnits, prevXPainterUnits, prevYPainterUnits,
588 thisXPainterUnits, thisYPainterUnits, xPainterUnits,
589 yPainterUnits, totalPoints, i );
590 }
591
592 if ( !visitPoint( pX, pY, pZ, pM, nextPointDistance, calculatedAngle ) )
593 return;
594
595 nextPointDistance += distance;
596 }
597
598 distanceTraversed += segmentLength;
599 prevX = thisX;
600 prevY = thisY;
601 prevZ = thisZ;
602 prevM = thisM;
603 prevXPainterUnits = thisXPainterUnits;
604 prevYPainterUnits = thisYPainterUnits;
605 }
606}
607
608double interpolateValue( double a, double b, double fraction )
609{
610 return a + ( b - a ) * fraction;
611}
612
613
614void visitPointsByInterpolatedZM( const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double step, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint, bool useZ )
615{
616 if ( step < 0 )
617 return;
618
619 double distanceTraversed = 0;
620 const int totalPoints = line->numPoints();
621 if ( totalPoints < 2 )
622 return;
623
624 const double *x = line->xData();
625 const double *y = line->yData();
626 const double *z = line->is3D() ? line->zData() : nullptr;
627 const double *m = line->isMeasure() ? line->mData() : nullptr;
628
629 const double *xPainterUnits = linePainterUnits->xData();
630 const double *yPainterUnits = linePainterUnits->yData();
631
632 double prevX = *x++;
633 double prevY = *y++;
634 double prevZ = z ? *z++ : 0.0;
635 double prevM = m ? *m++ : 0.0;
636
637 double prevXPainterUnits = *xPainterUnits++;
638 double prevYPainterUnits = *yPainterUnits++;
639
640 if ( qgsDoubleNear( step, 0.0 ) )
641 {
642 visitPoint( prevX, prevY, prevZ, prevM, 0, 0 );
643 return;
644 }
645
646 double prevValue = useZ ? prevZ : prevM;
647 bool isFirstPoint = true;
648 for ( int i = 1; i < totalPoints; ++i )
649 {
650 double thisX = *x++;
651 double thisY = *y++;
652 double thisZ = z ? *z++ : 0.0;
653 double thisM = m ? *m++ : 0.0;
654 const double thisValue = useZ ? thisZ : thisM;
655 double thisXPainterUnits = *xPainterUnits++;
656 double thisYPainterUnits = *yPainterUnits++;
657
658 double angle = std::fmod( QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 );
659 if ( angle > 90 && angle < 270 )
660 angle += 180;
661
662 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
663 const double segmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( thisXPainterUnits, thisYPainterUnits, prevXPainterUnits, prevYPainterUnits );
664
665 // direction for this segment
666 const int direction = ( thisValue > prevValue ) ? 1 : ( thisValue < prevValue ) ? -1 : 0;
667 if ( direction != 0 )
668 {
669 // non-constant segment
670 double nextStepValue = direction > 0 ? std::ceil( prevValue / step ) * step
671 : std::floor( prevValue / step ) * step;
672
673 while ( ( direction > 0 && ( nextStepValue <= thisValue || qgsDoubleNear( nextStepValue, thisValue ) ) ) ||
674 ( direction < 0 && ( nextStepValue >= thisValue || qgsDoubleNear( nextStepValue, thisValue ) ) ) )
675 {
676 const double targetPointFractionAlongSegment = ( nextStepValue - prevValue ) / ( thisValue - prevValue );
677 const double targetPointDistanceAlongSegment = targetPointFractionAlongSegment * segmentLengthPainterUnits;
678
679 double pX, pY;
680 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, targetPointFractionAlongSegment * segmentLength, pX, pY );
681
682 const double pZ = useZ ? nextStepValue : interpolateValue( prevZ, thisZ, targetPointFractionAlongSegment );
683 const double pM = useZ ? interpolateValue( prevM, thisM, targetPointFractionAlongSegment ) : nextStepValue;
684
685 double calculatedAngle = angle;
686 if ( averageAngleLengthPainterUnits > 0 )
687 {
688 calculatedAngle = calculateAveragedAngle(
689 targetPointDistanceAlongSegment,
690 segmentLengthPainterUnits, averageAngleLengthPainterUnits,
691 prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits,
692 xPainterUnits, yPainterUnits,
693 totalPoints, i );
694 }
695
696 if ( !qgsDoubleNear( targetPointFractionAlongSegment, 0 ) || isFirstPoint )
697 {
698 if ( !visitPoint( pX, pY, pZ, pM, distanceTraversed + segmentLength * targetPointFractionAlongSegment, calculatedAngle ) )
699 return;
700 }
701
702 nextStepValue += direction * step;
703 }
704 }
705 else if ( isFirstPoint && emitFirstPoint )
706 {
707 if ( !visitPoint( prevX, prevY, prevZ, prevM, distanceTraversed,
708 std::fmod( QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 ) ) )
709 return;
710 }
711 isFirstPoint = false;
712
713 prevX = thisX;
714 prevY = thisY;
715 prevZ = thisZ;
716 prevM = thisM;
717 prevXPainterUnits = thisXPainterUnits;
718 prevYPainterUnits = thisYPainterUnits;
719 prevValue = thisValue;
720 distanceTraversed += segmentLength;
721 }
722}
723
724void visitPointsByInterpolatedZ( const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint )
725{
726 visitPointsByInterpolatedZM( line, linePainterUnits, emitFirstPoint, distance, averageAngleLengthPainterUnits, visitPoint, true );
727}
728
729void visitPointsByInterpolatedM( const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint )
730{
731 visitPointsByInterpolatedZM( line, linePainterUnits, emitFirstPoint, distance, averageAngleLengthPainterUnits, visitPoint, false );
732}
733
734QPointF QgsLinearReferencingSymbolLayer::pointToPainter( QgsSymbolRenderContext &context, double x, double y, double z )
735{
736 QPointF pt;
737 if ( context.renderContext().coordinateTransform().isValid() )
738 {
740 pt = QPointF( x, y );
741
742 }
743 else
744 {
745 pt = QPointF( x, y );
746 }
747
748 context.renderContext().mapToPixel().transformInPlace( pt.rx(), pt.ry() );
749 return pt;
750}
751
752void QgsLinearReferencingSymbolLayer::renderPolylineInterval( const QgsLineString *line, QgsSymbolRenderContext &context, double skipMultiples, const QPointF &labelOffsetPainterUnits, double averageAngleLengthPainterUnits, bool showMarker )
753{
754 double distance = mInterval;
756 {
757 context.setOriginalValueVariable( mInterval );
759 }
760
761 QgsNumericFormatContext numericContext;
762 numericContext.setExpressionContext( context.renderContext().expressionContext() );
763
764 std::unique_ptr< QgsLineString > painterUnitsGeometry( line->clone() );
765 if ( context.renderContext().coordinateTransform().isValid() )
766 {
767 painterUnitsGeometry->transform( context.renderContext().coordinateTransform() );
768 }
769 painterUnitsGeometry->transform( context.renderContext().mapToPixel().transform() );
770
771 const bool hasZ = line->is3D();
772 const bool hasM = line->isMeasure();
773 const bool emitFirstPoint = mLabelSource != Qgis::LinearReferencingLabelSource::CartesianDistance2D;
774
775 VisitPointAtDistanceFunction func = nullptr;
776
777 switch ( mPlacement )
778 {
781 break;
782
785 break;
786
789 break;
790
792 return;
793 }
794
795 QgsLinearReferencingSymbolLayerLabelProvider *labelProvider = nullptr;
796 if ( QgsLabelingEngine *labelingEngine = context.renderContext().labelingEngine() )
797 {
798 // rendering with a labeling engine. In this scenario we will register rendered text as labels, so that they participate in the labeling problem
799 // for the map
800 labelProvider = qgis::down_cast< QgsLinearReferencingSymbolLayerLabelProvider * >( labelingEngine->providerById( mLabelProviderId ) );
801 }
802
803 func( line, painterUnitsGeometry.get(), emitFirstPoint, distance, averageAngleLengthPainterUnits, [&context, &numericContext, skipMultiples, showMarker,
804 labelOffsetPainterUnits, hasZ, hasM, labelProvider, this]( double x, double y, double z, double m, double distanceFromStart, double angle ) -> bool
805 {
806 if ( context.renderContext().renderingStopped() )
807 return false;
808
809 double labelValue = 0;
810 bool labelVertex = true;
811 switch ( mLabelSource )
812 {
813 case Qgis::LinearReferencingLabelSource::CartesianDistance2D:
814 labelValue = distanceFromStart;
815 break;
816 case Qgis::LinearReferencingLabelSource::Z:
817 labelValue = z;
818 labelVertex = hasZ && !std::isnan( labelValue );
819 break;
820 case Qgis::LinearReferencingLabelSource::M:
821 labelValue = m;
822 labelVertex = hasM && !std::isnan( labelValue );
823 break;
824 }
825
826 if ( !labelVertex )
827 return true;
828
829 if ( skipMultiples > 0 && qgsDoubleNear( std::fmod( labelValue, skipMultiples ), 0 ) )
830 return true;
831
832 const QPointF pt = pointToPainter( context, x, y, z );
833
834 if ( mMarkerSymbol && showMarker )
835 {
836 if ( mRotateLabels )
837 mMarkerSymbol->setLineAngle( 90 - angle );
838 mMarkerSymbol->renderPoint( pt, context.feature(), context.renderContext() );
839 }
840
841 const double angleRadians = ( mRotateLabels ? angle : 0 ) * M_PI / 180.0;
842 const double dx = labelOffsetPainterUnits.x() * std::sin( angleRadians + M_PI_2 )
843 + labelOffsetPainterUnits.y() * std::sin( angleRadians );
844 const double dy = labelOffsetPainterUnits.x() * std::cos( angleRadians + M_PI_2 )
845 + labelOffsetPainterUnits.y() * std::cos( angleRadians );
846
847 const QString text = mNumericFormat->formatDouble( labelValue, numericContext );
848 if ( !labelProvider )
849 {
850 // render text directly
851 QgsTextRenderer::drawText( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, Qgis::TextHorizontalAlignment::Left, { text }, context.renderContext(), mTextFormat );
852 }
853 else
854 {
855 // register as a label
856 labelProvider->addLabel(
857 QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, text, context.renderContext(), mTextFormat
858 );
859 }
860
861 return true;
862 } );
863}
864
865void QgsLinearReferencingSymbolLayer::renderPolylineVertex( const QgsLineString *line, QgsSymbolRenderContext &context, double skipMultiples, const QPointF &labelOffsetPainterUnits, double averageAngleLengthPainterUnits, bool showMarker )
866{
867 // let's simplify the logic by ALWAYS using the averaging approach for angles, and just
868 // use a very small distance if the user actually set this to 0. It'll be identical
869 // results anyway...
870 averageAngleLengthPainterUnits = std::max( averageAngleLengthPainterUnits, 0.1 );
871
872 QgsNumericFormatContext numericContext;
873 numericContext.setExpressionContext( context.renderContext().expressionContext() );
874
875 QgsLinearReferencingSymbolLayerLabelProvider *labelProvider = nullptr;
876 if ( QgsLabelingEngine *labelingEngine = context.renderContext().labelingEngine() )
877 {
878 // rendering with a labeling engine. In this scenario we will register rendered text as labels, so that they participate in the labeling problem
879 // for the map
880 labelProvider = qgis::down_cast< QgsLinearReferencingSymbolLayerLabelProvider * >( labelingEngine->providerById( mLabelProviderId ) );
881 }
882
883 const double *xData = line->xData();
884 const double *yData = line->yData();
885 const double *zData = line->is3D() ? line->zData() : nullptr;
886 const double *mData = line->isMeasure() ? line->mData() : nullptr;
887 const int size = line->numPoints();
888 if ( size < 2 )
889 return;
890
891 std::unique_ptr< QgsLineString > painterUnitsGeometry( line->clone() );
892 if ( context.renderContext().coordinateTransform().isValid() )
893 {
894 painterUnitsGeometry->transform( context.renderContext().coordinateTransform() );
895 }
896 painterUnitsGeometry->transform( context.renderContext().mapToPixel().transform() );
897 const double *xPainterUnits = painterUnitsGeometry->xData();
898 const double *yPainterUnits = painterUnitsGeometry->yData();
899
900 double currentDistance = 0;
901 double prevX = *xData;
902 double prevY = *yData;
903
904 for ( int i = 0; i < size; ++i )
905 {
906 if ( context.renderContext().renderingStopped() )
907 break;
908
909 double thisX = *xData++;
910 double thisY = *yData++;
911 double thisZ = zData ? *zData++ : 0;
912 double thisM = mData ? *mData++ : 0;
913 double thisXPainterUnits = *xPainterUnits++;
914 double thisYPainterUnits = *yPainterUnits++;
915
916 const double thisSegmentLength = QgsGeometryUtilsBase::distance2D( prevX, prevY, thisX, thisY );
917 currentDistance += thisSegmentLength;
918
919 if ( skipMultiples > 0 && qgsDoubleNear( std::fmod( currentDistance, skipMultiples ), 0 ) )
920 {
921 prevX = thisX;
922 prevY = thisY;
923 continue;
924 }
925
926 const QPointF pt = pointToPainter( context, thisX, thisY, thisZ );
927
928 // track forward by averageAngleLengthPainterUnits
929 double painterDistRemaining = averageAngleLengthPainterUnits;
930 double startAverageSegmentX = thisXPainterUnits;
931 double startAverageSegmentY = thisYPainterUnits;
932
933 const double *xAveragingData = xPainterUnits;
934 const double *yAveragingData = yPainterUnits;
935 double endAverageSegmentX = *xAveragingData;
936 double endAverageSegmentY = *yAveragingData;
937 double averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
938
939 int j = i;
940 while ( ( j < size - 1 ) && ( painterDistRemaining > averagingSegmentLengthPainterUnits ) )
941 {
942 painterDistRemaining -= averagingSegmentLengthPainterUnits;
943 startAverageSegmentX = endAverageSegmentX;
944 startAverageSegmentY = endAverageSegmentY;
945
946 endAverageSegmentX = *xAveragingData++;
947 endAverageSegmentY = *yAveragingData++;
948 j++;
949 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
950 }
951 // fits on this same segment
952 double endAverageXPainterUnits = thisXPainterUnits;
953 double endAverageYPainterUnits = thisYPainterUnits;
954 if ( ( j < size - 1 ) && painterDistRemaining < averagingSegmentLengthPainterUnits )
955 {
956 QgsGeometryUtilsBase::pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, endAverageXPainterUnits, endAverageYPainterUnits,
957 nullptr, nullptr, nullptr,
958 nullptr, nullptr, nullptr );
959 }
960 else if ( i < size - 2 )
961 {
962 endAverageXPainterUnits = endAverageSegmentX;
963 endAverageYPainterUnits = endAverageSegmentY;
964 }
965
966 // also track back by averageAngleLengthPainterUnits
967 j = i;
968 painterDistRemaining = averageAngleLengthPainterUnits;
969 startAverageSegmentX = thisXPainterUnits;
970 startAverageSegmentY = thisYPainterUnits;
971
972 xAveragingData = xPainterUnits - 2;
973 yAveragingData = yPainterUnits - 2;
974
975 endAverageSegmentX = *xAveragingData;
976 endAverageSegmentY = *yAveragingData;
977 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
978
979 while ( j > 0 && painterDistRemaining > averagingSegmentLengthPainterUnits )
980 {
981 painterDistRemaining -= averagingSegmentLengthPainterUnits;
982 startAverageSegmentX = endAverageSegmentX;
983 startAverageSegmentY = endAverageSegmentY;
984
985 endAverageSegmentX = *xAveragingData--;
986 endAverageSegmentY = *yAveragingData--;
987 j--;
988 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
989 }
990 // fits on this same segment
991 double startAverageXPainterUnits = thisXPainterUnits;
992 double startAverageYPainterUnits = thisYPainterUnits;
993 if ( j > 0 && painterDistRemaining < averagingSegmentLengthPainterUnits )
994 {
995 QgsGeometryUtilsBase::pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, startAverageXPainterUnits, startAverageYPainterUnits,
996 nullptr, nullptr, nullptr,
997 nullptr, nullptr, nullptr );
998 }
999 else if ( j > 1 )
1000 {
1001 startAverageXPainterUnits = endAverageSegmentX;
1002 startAverageYPainterUnits = endAverageSegmentY;
1003 }
1004
1005 double calculatedAngle = std::fmod( QgsGeometryUtilsBase::azimuth( startAverageXPainterUnits, startAverageYPainterUnits, endAverageXPainterUnits, endAverageYPainterUnits ) + 360, 360 );
1006
1007 if ( calculatedAngle > 90 && calculatedAngle < 270 )
1008 calculatedAngle += 180;
1009
1010 if ( mMarkerSymbol && showMarker )
1011 {
1012 if ( mRotateLabels )
1013 mMarkerSymbol->setLineAngle( 90 - calculatedAngle );
1014 mMarkerSymbol->renderPoint( pt, context.feature(), context.renderContext() );
1015 }
1016
1017 const double angleRadians = mRotateLabels ? ( calculatedAngle * M_PI / 180.0 ) : 0;
1018 const double dx = labelOffsetPainterUnits.x() * std::sin( angleRadians + M_PI_2 )
1019 + labelOffsetPainterUnits.y() * std::sin( angleRadians );
1020 const double dy = labelOffsetPainterUnits.x() * std::cos( angleRadians + M_PI_2 )
1021 + labelOffsetPainterUnits.y() * std::cos( angleRadians );
1022
1023 double labelValue = 0;
1024 bool labelVertex = true;
1025 switch ( mLabelSource )
1026 {
1028 labelValue = currentDistance;
1029 break;
1031 labelValue = thisZ;
1032 labelVertex = static_cast< bool >( zData ) && !std::isnan( labelValue );
1033 break;
1035 labelValue = thisM;
1036 labelVertex = static_cast< bool >( mData ) && !std::isnan( labelValue );
1037 break;
1038 }
1039
1040 if ( !labelVertex )
1041 continue;
1042
1043 const QString text = mNumericFormat->formatDouble( labelValue, numericContext );
1044 if ( !labelProvider )
1045 {
1046 // render text directly
1047 QgsTextRenderer::drawText( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, Qgis::TextHorizontalAlignment::Left, { text }, context.renderContext(), mTextFormat );
1048 }
1049 else
1050 {
1051 // register as a label
1052 labelProvider->addLabel(
1053 QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, text, context.renderContext(), mTextFormat
1054 );
1055 }
1056 prevX = thisX;
1057 prevY = thisY;
1058 }
1059}
1060
1062{
1063 return mTextFormat;
1064}
1065
1067{
1068 mTextFormat = format;
1069}
1070
1072{
1073 return mNumericFormat.get();
1074}
1075
1077{
1078 mNumericFormat.reset( format );
1079}
1080
1082{
1083 return mInterval;
1084}
1085
1087{
1088 mInterval = interval;
1089}
1090
1092{
1093 return mSkipMultiplesOf;
1094}
1095
1097{
1098 mSkipMultiplesOf = skipMultiplesOf;
1099}
1100
1102{
1103 return mShowMarker;
1104}
1105
1107{
1108 mShowMarker = show;
1109 if ( show && !mMarkerSymbol )
1110 {
1111 mMarkerSymbol.reset( QgsMarkerSymbol::createSimple( {} ) );
1112 }
1113}
1114
1119
1124
1129
1134
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ DisableFeatureClipping
If present, indicates that features should never be clipped to the map extent during rendering.
@ AffectsLabeling
If present, indicates that the symbol layer will participate in the map labeling problem.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:821
@ Point
Text at point of origin layout mode.
@ Horizontal
Horizontally oriented text.
LinearReferencingPlacement
Defines how/where the labels should be placed in a linear referencing symbol layer.
Definition qgis.h:2971
@ IntervalZ
Place labels at regular intervals, linearly interpolated using Z values.
@ Vertex
Place labels on every vertex in the line.
@ IntervalM
Place labels at regular intervals, linearly interpolated using M values.
@ IntervalCartesian2D
Place labels at regular intervals, using Cartesian distance calculations on a 2D plane.
LinearReferencingLabelSource
Defines what quantity to use for the labels shown in a linear referencing symbol layer.
Definition qgis.h:2985
@ CartesianDistance2D
Distance along line, calculated using Cartesian calculations on a 2D plane.
QFlags< SymbolRenderHint > SymbolRenderHints
Symbol render hints.
Definition qgis.h:741
@ Marker
Marker symbol.
Abstract base class for all geometries.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary const part after the last part of the geometry.
const_part_iterator const_parts_begin() const
Returns STL-style iterator pointing to the const first part of the geometry.
The QgsAbstractLabelProvider class is an interface class.
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context)=0
Returns list of label features (they are owned by the provider and thus deleted on its destruction)
virtual void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const =0
Draw this label at the position determined by the labeling engine.
@ DrawLabels
Whether the labels should be rendered.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
static QgsNumericFormatRegistry * numericFormatRegistry()
Gets the registry of available numeric formats.
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double azimuth(double x1, double y1, double x2, double y2)
Calculates Cartesian azimuth between points (x1, y1) and (x2, y2) (clockwise in degree,...
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlags())
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition qgsgeos.cpp:257
The QgsLabelingEngine class provides map labeling functionality.
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.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
int numPoints() const override
Returns the number of points in the curve.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Abstract base class for line symbol layers.
Line symbol layer used for decorating accordingly to linear referencing.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setShowMarker(bool show)
Sets whether a marker symbol should be shown corresponding to the labeled point on line.
void setSkipMultiplesOf(double multiple)
Sets the multiple distance to skip labels for.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
QgsTextFormat textFormat() const
Returns the text format used to render the layer.
QString layerType() const override
Returns a string that represents this layer type.
bool showMarker() const
Returns true if a marker symbol should be shown corresponding to the labeled point on line.
void setNumericFormat(QgsNumericFormat *format)
Sets the numeric format used to format the labels for the layer.
QgsNumericFormat * numericFormat() const
Returns the numeric format used to format the labels for the layer.
void setInterval(double interval)
Sets the interval between labels.
void setLabelSource(Qgis::LinearReferencingLabelSource source)
Sets the label source, which dictates what quantity to use for the labels shown.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used to render the layer.
Qgis::LinearReferencingLabelSource labelSource() const
Returns the label source, which dictates what quantity to use for the labels shown.
double skipMultiplesOf() const
Returns the multiple distance to skip labels for.
QgsLinearReferencingSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setPlacement(Qgis::LinearReferencingPlacement placement)
Sets the placement mode for the labels.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsLinearReferencingSymbolLayer, using the specified properties.
double interval() const
Returns the interval between labels.
Qgis::LinearReferencingPlacement placement() const
Returns the placement mode for the labels.
Perform transforms between map coordinates and device coordinates.
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
void transformInPlace(double &x, double &y) const
Transforms map coordinates to device coordinates.
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
A context for numeric formats.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context to use when evaluating QgsExpressions.
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:165
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr).
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
const QgsAbstractGeometry * geometry() const
Returns pointer to the unsegmentized geometry.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
@ SkipMultiples
Skip multiples of.
@ ShowMarker
Show markers.
@ AverageAngleLength
Length to average symbol angles over.
@ Interval
Line marker interval.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
Contains pre-calculated metrics of a QgsTextDocument.
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0, const QgsTextDocumentRenderContext &documentContext=QgsTextDocumentRenderContext())
Returns precalculated text metrics for a text document, when rendered using the given base format and...
Represents a document consisting of one or more QgsTextBlock objects.
static QgsTextDocument fromTextAndFormat(const QStringList &lines, const QgsTextFormat &format)
Constructor for QgsTextDocument consisting of a set of lines, respecting settings from a text format.
Container for all settings relating to text rendering.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
Class that adds extra information to QgsLabelFeature for text labels.
static void drawDocument(const QRectF &rect, const QgsTextFormat &format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment horizontalAlignment=Qgis::TextHorizontalAlignment::Left, Qgis::TextVerticalAlignment verticalAlignment=Qgis::TextVerticalAlignment::Top, double rotation=0, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags())
Draws a text document within a rectangle using the specified settings.
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
LabelPosition is a candidate feature label position.
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)
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6234
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6215
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6024
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
std::function< bool(double x, double y, double z, double m, double distanceFromStart, double angle)> VisitPointFunction
void visitPointsByInterpolatedM(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint)
void visitPointsByRegularDistance(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint)
double calculateAveragedAngle(double targetPointDistanceAlongSegment, double segmentLengthPainterUnits, double averageAngleLengthPainterUnits, double prevXPainterUnits, double prevYPainterUnits, double thisXPainterUnits, double thisYPainterUnits, const double *xPainterUnits, const double *yPainterUnits, int totalPoints, int i)
std::function< void(const QgsLineString *, const QgsLineString *, bool, double, double, const VisitPointFunction &) > VisitPointAtDistanceFunction
void visitPointsByInterpolatedZM(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double step, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint, bool useZ)
void visitPointsByInterpolatedZ(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint)
double interpolateValue(double a, double b, double fraction)