QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
18#include "feature.h"
19#include "labelposition.h"
20#include "qgsapplication.h"
22#include "qgsgeometryutils.h"
23#include "qgsgeos.h"
24#include "qgslinestring.h"
25#include "qgsmarkersymbol.h"
27#include "qgspallabeling.h"
28#include "qgspolygon.h"
29#include "qgsrendercontext.h"
30#include "qgssymbollayerutils.h"
31#include "qgstextlabelfeature.h"
32#include "qgstextrenderer.h"
33#include "qgsunittypes.h"
34
35#include <QString>
36
37using namespace Qt::StringLiterals;
38
40class QgsTextLabelFeatureWithFormat : public QgsTextLabelFeature
41{
42 public:
43 QgsTextLabelFeatureWithFormat( QgsFeatureId id, geos::unique_ptr geometry, QSizeF size, const QgsTextFormat &format )
44 : QgsTextLabelFeature( id, std::move( geometry ), size )
45 , mFormat( format )
46 {}
47
48 QgsTextFormat mFormat;
49};
50
51class QgsLinearReferencingSymbolLayerLabelProvider final : public QgsAbstractLabelProvider
52{
53 public:
54 QgsLinearReferencingSymbolLayerLabelProvider()
55 : QgsAbstractLabelProvider( nullptr )
56 {
58 mFlags |= DrawLabels;
59
60 // always consider these labels highest priority
61 // TODO possibly expose as a setting for the symbol layer?
62 mPriority = 0;
63 }
64
65 ~QgsLinearReferencingSymbolLayerLabelProvider() override { qDeleteAll( mLabels ); }
66
67 void addLabel( const QPointF &painterPoint, double angleRadians, const QString &text, QgsRenderContext &context, const QgsTextFormat &format )
68 {
69 // labels must be registered in destination map units
70 QgsPoint mapPoint( painterPoint );
71 mapPoint.transform( context.mapToPixel().transform().inverted() );
72
73 const QgsTextDocument doc = QgsTextDocument::fromTextAndFormat( { text }, format );
74 QgsTextDocumentMetrics documentMetrics = QgsTextDocumentMetrics::calculateMetrics( doc, format, context );
75 const QSizeF size = documentMetrics.documentSize( Qgis::TextLayoutMode::Point, Qgis::TextOrientation::Horizontal );
76
77 double uPP = context.mapToPixel().mapUnitsPerPixel();
78 auto feature = std::make_unique< QgsTextLabelFeatureWithFormat >( mLabels.size(), 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 { return mLabels; }
91
92 void drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const final
93 {
94 // as per vector label rendering...
95 QgsMapToPixel xform = context.mapToPixel();
96 xform.setMapRotation( 0, 0, 0 );
97 const QPointF outPt = context.mapToPixel().transform( label->getX(), label->getY() ).toQPointF();
98
99 QgsTextLabelFeatureWithFormat *lf = qgis::down_cast<QgsTextLabelFeatureWithFormat *>( label->getFeaturePart()->feature() );
100 QgsTextRenderer::drawDocument( outPt, lf->mFormat, lf->document(), lf->documentMetrics(), context, Qgis::TextHorizontalAlignment::Left, label->getAlpha() );
101 }
102
103 private:
104 QList<QgsLabelFeature *> mLabels;
105};
107
110{
111 mNumericFormat = std::make_unique< QgsBasicNumericFormat >();
112}
113
115
117{
118 auto res = std::make_unique< QgsLinearReferencingSymbolLayer >();
119 res->setPlacement( qgsEnumKeyToValue( properties.value( u"placement"_s ).toString(), Qgis::LinearReferencingPlacement::IntervalCartesian2D ) );
120 res->setLabelSource( qgsEnumKeyToValue( properties.value( u"source"_s ).toString(), Qgis::LinearReferencingLabelSource::CartesianDistance2D ) );
121 bool ok = false;
122 const double interval = properties.value( u"interval"_s ).toDouble( &ok );
123 if ( ok )
124 res->setInterval( interval );
125 const double skipMultiples = properties.value( u"skip_multiples"_s ).toDouble( &ok );
126 if ( ok )
127 res->setSkipMultiplesOf( skipMultiples );
128 res->setRotateLabels( properties.value( u"rotate"_s, true ).toBool() );
129 res->setShowMarker( properties.value( u"show_marker"_s, false ).toBool() );
130
131 // it's impossible to get the project's path resolver here :(
132 // TODO QGIS 5.0 -- force use of QgsReadWriteContext in create methods
133 QgsReadWriteContext rwContext;
134 //rwContext.setPathResolver( QgsProject::instance()->pathResolver() ); // skip-keyword-check
135
136 const QString textFormatXml = properties.value( u"text_format"_s ).toString();
137 if ( !textFormatXml.isEmpty() )
138 {
139 QDomDocument doc;
140 QDomElement elem;
141 doc.setContent( textFormatXml );
142 elem = doc.documentElement();
143
145 textFormat.readXml( elem, rwContext );
146 res->setTextFormat( textFormat );
147 }
148
149 const QString numericFormatXml = properties.value( u"numeric_format"_s ).toString();
150 if ( !numericFormatXml.isEmpty() )
151 {
152 QDomDocument doc;
153 doc.setContent( numericFormatXml );
154 res->setNumericFormat( QgsApplication::numericFormatRegistry()->createFromXml( doc.documentElement(), rwContext ) );
155 }
156
157 if ( properties.contains( u"label_offset"_s ) )
158 {
159 res->setLabelOffset( QgsSymbolLayerUtils::decodePoint( properties[u"label_offset"_s].toString() ) );
160 }
161 if ( properties.contains( u"label_offset_unit"_s ) )
162 {
163 res->setLabelOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[u"label_offset_unit"_s].toString() ) );
164 }
165 if ( properties.contains( ( u"label_offset_map_unit_scale"_s ) ) )
166 {
167 res->setLabelOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"label_offset_map_unit_scale"_s].toString() ) );
168 }
169 if ( properties.contains( u"average_angle_length"_s ) )
170 {
171 res->setAverageAngleLength( properties[u"average_angle_length"_s].toDouble() );
172 }
173 if ( properties.contains( u"average_angle_unit"_s ) )
174 {
175 res->setAverageAngleUnit( QgsUnitTypes::decodeRenderUnit( properties[u"average_angle_unit"_s].toString() ) );
176 }
177 if ( properties.contains( ( u"average_angle_map_unit_scale"_s ) ) )
178 {
179 res->setAverageAngleMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"average_angle_map_unit_scale"_s].toString() ) );
180 }
181
182 return res.release();
183}
184
186{
187 auto res = std::make_unique< QgsLinearReferencingSymbolLayer >();
188 res->setPlacement( mPlacement );
189 res->setLabelSource( mLabelSource );
190 res->setInterval( mInterval );
191 res->setSkipMultiplesOf( mSkipMultiplesOf );
192 res->setRotateLabels( mRotateLabels );
193 res->setLabelOffset( mLabelOffset );
194 res->setLabelOffsetUnit( mLabelOffsetUnit );
195 res->setLabelOffsetMapUnitScale( mLabelOffsetMapUnitScale );
196 res->setShowMarker( mShowMarker );
197 res->setAverageAngleLength( mAverageAngleLength );
198 res->setAverageAngleUnit( mAverageAngleLengthUnit );
199 res->setAverageAngleMapUnitScale( mAverageAngleLengthMapUnitScale );
200
201 res->mTextFormat = mTextFormat;
202 res->mMarkerSymbol.reset( mMarkerSymbol ? mMarkerSymbol->clone() : nullptr );
203 if ( mNumericFormat )
204 res->mNumericFormat.reset( mNumericFormat->clone() );
205
206 copyCommonProperties( res.get() );
207
208 return res.release();
209}
210
212{
213 QDomDocument textFormatDoc;
214 // it's impossible to get the project's path resolver here :(
215 // TODO QGIS 5.0 -- force use of QgsReadWriteContext in properties methods
216 QgsReadWriteContext rwContext;
217 // rwContext.setPathResolver( QgsProject::instance()->pathResolver() ); // skip-keyword-check
218 const QDomElement textElem = mTextFormat.writeXml( textFormatDoc, rwContext );
219 textFormatDoc.appendChild( textElem );
220
221 QDomDocument numericFormatDoc;
222 QDomElement numericFormatElem = numericFormatDoc.createElement( u"numericFormat"_s );
223 mNumericFormat->writeXml( numericFormatElem, numericFormatDoc, rwContext );
224 numericFormatDoc.appendChild( numericFormatElem );
225
226 QVariantMap res {
227 { u"placement"_s, qgsEnumValueToKey( mPlacement ) },
228 { u"source"_s, qgsEnumValueToKey( mLabelSource ) },
229 { u"interval"_s, mInterval },
230 { u"rotate"_s, mRotateLabels },
231 { u"show_marker"_s, mShowMarker },
232 { u"text_format"_s, textFormatDoc.toString() },
233 { u"numeric_format"_s, numericFormatDoc.toString() },
234 { u"label_offset"_s, QgsSymbolLayerUtils::encodePoint( mLabelOffset ) },
235 { u"label_offset_unit"_s, QgsUnitTypes::encodeUnit( mLabelOffsetUnit ) },
236 { u"label_offset_map_unit_scale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( mLabelOffsetMapUnitScale ) },
237 { u"average_angle_length"_s, mAverageAngleLength },
238 { u"average_angle_unit"_s, QgsUnitTypes::encodeUnit( mAverageAngleLengthUnit ) },
239 { u"average_angle_map_unit_scale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( mAverageAngleLengthMapUnitScale ) },
240 };
241
242 if ( mSkipMultiplesOf >= 0 )
243 {
244 res.insert( u"skip_multiples"_s, mSkipMultiplesOf );
245 }
246
247 return res;
248}
249
251{
252 return u"LinearReferencing"_s;
253}
254
259
261{
262 return mShowMarker ? mMarkerSymbol.get() : nullptr;
263}
264
266{
267 if ( symbol && symbol->type() == Qgis::SymbolType::Marker )
268 {
269 mMarkerSymbol.reset( qgis::down_cast<QgsMarkerSymbol *>( symbol ) );
270 return true;
271 }
272 delete symbol;
273 return false;
274}
275
277{
278 if ( mMarkerSymbol )
279 {
280 Qgis::SymbolRenderHints hints = mMarkerSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol;
281 if ( mRotateLabels )
283 mMarkerSymbol->setRenderHints( hints );
284
285 mMarkerSymbol->startRender( context.renderContext(), context.fields() );
286 }
287
288 if ( QgsLabelingEngine *labelingEngine = context.renderContext().labelingEngine() )
289 {
290 // rendering with a labeling engine. In this scenario we will register rendered text as labels, so that they participate in the labeling problem
291 // for the map
292 QgsLinearReferencingSymbolLayerLabelProvider *provider = new QgsLinearReferencingSymbolLayerLabelProvider();
293 mLabelProviderId = labelingEngine->addProvider( provider );
294 }
295}
296
298{
299 if ( mMarkerSymbol )
300 {
301 mMarkerSymbol->stopRender( context.renderContext() );
302 }
303}
304
305void QgsLinearReferencingSymbolLayer::renderGeometryPart(
306 QgsSymbolRenderContext &context, const QgsAbstractGeometry *geometry, double labelOffsetPainterUnitsX, double labelOffsetPainterUnitsY, double skipMultiples, double averageAngleDistancePainterUnits, bool showMarker
307)
308{
309 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( geometry ) )
310 {
311 renderLineString( context, line, labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
312 }
313 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( geometry ) )
314 {
315 renderLineString( context, qgsgeometry_cast< const QgsLineString *>( polygon->exteriorRing() ), labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
316 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
317 {
318 renderLineString( context, qgsgeometry_cast< const QgsLineString *>( polygon->interiorRing( i ) ), labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
319 }
320 }
321}
322
323void QgsLinearReferencingSymbolLayer::renderLineString(
324 QgsSymbolRenderContext &context, const QgsLineString *line, double labelOffsetPainterUnitsX, double labelOffsetPainterUnitsY, double skipMultiples, double averageAngleDistancePainterUnits, bool showMarker
325)
326{
327 if ( !line )
328 return;
329
330 switch ( mPlacement )
331 {
335 renderPolylineInterval( line, context, skipMultiples, QPointF( labelOffsetPainterUnitsX, labelOffsetPainterUnitsY ), averageAngleDistancePainterUnits, showMarker );
336 break;
337
339 renderPolylineVertex( line, context, skipMultiples, QPointF( labelOffsetPainterUnitsX, labelOffsetPainterUnitsY ), averageAngleDistancePainterUnits, showMarker );
340 break;
341 }
342}
343
345{
346 QPainter *p = context.renderContext().painter();
347 if ( !p )
348 {
349 return;
350 }
351
352 // TODO (maybe?): if we don't have an original geometry, convert points to linestring and scale distance to painter units?
353 // in reality this line type makes no sense for rendering non-real feature geometries...
354 ( void ) points;
355 const QgsAbstractGeometry *geometry = context.renderContext().geometry();
356 if ( !geometry )
357 return;
358
359 double skipMultiples = mSkipMultiplesOf;
361 {
362 context.setOriginalValueVariable( mSkipMultiplesOf );
363 skipMultiples = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::SkipMultiples, context.renderContext().expressionContext(), mSkipMultiplesOf );
364 }
365
366 double labelOffsetX = mLabelOffset.x();
367 double labelOffsetY = mLabelOffset.y();
368
369 double averageOver = mAverageAngleLength;
371 {
372 context.setOriginalValueVariable( mAverageAngleLength );
373 averageOver = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::AverageAngleLength, context.renderContext().expressionContext(), mAverageAngleLength );
374 }
375
376 bool showMarker = mShowMarker;
378 {
381 }
382
383 const double labelOffsetPainterUnitsX = context.renderContext().convertToPainterUnits( labelOffsetX, mLabelOffsetUnit, mLabelOffsetMapUnitScale );
384 const double labelOffsetPainterUnitsY = context.renderContext().convertToPainterUnits( labelOffsetY, mLabelOffsetUnit, mLabelOffsetMapUnitScale );
385 const double averageAngleDistancePainterUnits = context.renderContext().convertToPainterUnits( averageOver, mAverageAngleLengthUnit, mAverageAngleLengthMapUnitScale ) / 2;
386
387 for ( auto partIt = geometry->const_parts_begin(); partIt != geometry->const_parts_end(); ++partIt )
388 {
389 renderGeometryPart( context, *partIt, labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits, showMarker );
390 }
391}
392
393
395 double targetPointDistanceAlongSegment,
396 double segmentLengthPainterUnits,
397 double averageAngleLengthPainterUnits,
398 double prevXPainterUnits,
399 double prevYPainterUnits,
400 double thisXPainterUnits,
401 double thisYPainterUnits,
402 const double *xPainterUnits,
403 const double *yPainterUnits,
404 int totalPoints,
405 int i
406)
407{
408 // track forward by averageAngleLengthPainterUnits
409 double painterDistRemaining = averageAngleLengthPainterUnits + targetPointDistanceAlongSegment;
410 double startAverageSegmentX = prevXPainterUnits;
411 double startAverageSegmentY = prevYPainterUnits;
412 double endAverageSegmentX = thisXPainterUnits;
413 double endAverageSegmentY = thisYPainterUnits;
414 double averagingSegmentLengthPainterUnits = segmentLengthPainterUnits;
415 const double *xAveragingData = xPainterUnits;
416 const double *yAveragingData = yPainterUnits;
417
418 int j = i;
419 while ( painterDistRemaining > averagingSegmentLengthPainterUnits )
420 {
421 if ( j >= totalPoints - 1 )
422 break;
423
424 painterDistRemaining -= averagingSegmentLengthPainterUnits;
425 startAverageSegmentX = endAverageSegmentX;
426 startAverageSegmentY = endAverageSegmentY;
427
428 endAverageSegmentX = *xAveragingData++;
429 endAverageSegmentY = *yAveragingData++;
430 j++;
431 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
432 }
433 // fits on this same segment
434 double endAverageXPainterUnits;
435 double endAverageYPainterUnits;
436 if ( painterDistRemaining < averagingSegmentLengthPainterUnits )
437 {
439 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, endAverageXPainterUnits, endAverageYPainterUnits, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr );
440 }
441 else
442 {
443 endAverageXPainterUnits = endAverageSegmentX;
444 endAverageYPainterUnits = endAverageSegmentY;
445 }
446
447 // also track back by averageAngleLengthPainterUnits
448 j = i;
449 painterDistRemaining = ( segmentLengthPainterUnits - targetPointDistanceAlongSegment ) + averageAngleLengthPainterUnits;
450 startAverageSegmentX = thisXPainterUnits;
451 startAverageSegmentY = thisYPainterUnits;
452 endAverageSegmentX = prevXPainterUnits;
453 endAverageSegmentY = prevYPainterUnits;
454 averagingSegmentLengthPainterUnits = segmentLengthPainterUnits;
455 xAveragingData = xPainterUnits - 2;
456 yAveragingData = yPainterUnits - 2;
457 while ( painterDistRemaining > averagingSegmentLengthPainterUnits )
458 {
459 if ( j < 1 )
460 break;
461
462 painterDistRemaining -= averagingSegmentLengthPainterUnits;
463 startAverageSegmentX = endAverageSegmentX;
464 startAverageSegmentY = endAverageSegmentY;
465
466 endAverageSegmentX = *xAveragingData--;
467 endAverageSegmentY = *yAveragingData--;
468 j--;
469 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
470 }
471 // fits on this same segment
472 double startAverageXPainterUnits;
473 double startAverageYPainterUnits;
474 if ( painterDistRemaining < averagingSegmentLengthPainterUnits )
475 {
477 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, startAverageXPainterUnits, startAverageYPainterUnits, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr );
478 }
479 else
480 {
481 startAverageXPainterUnits = endAverageSegmentX;
482 startAverageYPainterUnits = endAverageSegmentY;
483 }
484
485 double calculatedAngle = std::fmod( QgsGeometryUtilsBase::azimuth( startAverageXPainterUnits, startAverageYPainterUnits, endAverageXPainterUnits, endAverageYPainterUnits ) + 360, 360 );
486 if ( calculatedAngle > 90 && calculatedAngle < 270 )
487 calculatedAngle += 180;
488
489 return calculatedAngle;
490}
491
492typedef std::function<bool( double x, double y, double z, double m, double distanceFromStart, double angle )> VisitPointFunction;
493typedef std::function< void( const QgsLineString *, const QgsLineString *, bool, double, double, const VisitPointFunction & ) > VisitPointAtDistanceFunction;
494
496 const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint
497)
498{
499 if ( distance < 0 )
500 return;
501
502 double distanceTraversed = 0;
503 const int totalPoints = line->numPoints();
504 if ( totalPoints == 0 )
505 return;
506
507 const double *x = line->xData();
508 const double *y = line->yData();
509 const double *z = line->is3D() ? line->zData() : nullptr;
510 const double *m = line->isMeasure() ? line->mData() : nullptr;
511
512 const double *xPainterUnits = linePainterUnits->xData();
513 const double *yPainterUnits = linePainterUnits->yData();
514
515 double prevX = *x++;
516 double prevY = *y++;
517 double prevZ = z ? *z++ : 0.0;
518 double prevM = m ? *m++ : 0.0;
519
520 double prevXPainterUnits = *xPainterUnits++;
521 double prevYPainterUnits = *yPainterUnits++;
522
523 if ( qgsDoubleNear( distance, 0.0 ) )
524 {
525 visitPoint( prevX, prevY, prevZ, prevM, 0, 0 );
526 return;
527 }
528
529 double pZ = std::numeric_limits<double>::quiet_NaN();
530 double pM = std::numeric_limits<double>::quiet_NaN();
531 double nextPointDistance = emitFirstPoint ? 0 : distance;
532 for ( int i = 1; i < totalPoints; ++i )
533 {
534 double thisX = *x++;
535 double thisY = *y++;
536 double thisZ = z ? *z++ : 0.0;
537 double thisM = m ? *m++ : 0.0;
538 double thisXPainterUnits = *xPainterUnits++;
539 double thisYPainterUnits = *yPainterUnits++;
540
541 double angle = std::fmod( QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 );
542 if ( angle > 90 && angle < 270 )
543 angle += 180;
544
545 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
546 const double segmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( thisXPainterUnits, thisYPainterUnits, prevXPainterUnits, prevYPainterUnits );
547
548 while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
549 {
550 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
551 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
552 double pX, pY;
554 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY, z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr, m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
555
556 double calculatedAngle = angle;
557 if ( averageAngleLengthPainterUnits > 0 )
558 {
559 const double targetPointFractionAlongSegment = distanceToPoint / segmentLength;
560 const double targetPointDistanceAlongSegment = targetPointFractionAlongSegment * segmentLengthPainterUnits;
561
562 calculatedAngle
563 = calculateAveragedAngle( targetPointDistanceAlongSegment, segmentLengthPainterUnits, averageAngleLengthPainterUnits, prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits, xPainterUnits, yPainterUnits, totalPoints, i );
564 }
565
566 if ( !visitPoint( pX, pY, pZ, pM, nextPointDistance, calculatedAngle ) )
567 return;
568
569 nextPointDistance += distance;
570 }
571
572 distanceTraversed += segmentLength;
573 prevX = thisX;
574 prevY = thisY;
575 prevZ = thisZ;
576 prevM = thisM;
577 prevXPainterUnits = thisXPainterUnits;
578 prevYPainterUnits = thisYPainterUnits;
579 }
580}
581
582double interpolateValue( double a, double b, double fraction )
583{
584 return a + ( b - a ) * fraction;
585}
586
587
589 const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double step, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint, bool useZ
590)
591{
592 if ( step < 0 )
593 return;
594
595 double distanceTraversed = 0;
596 const int totalPoints = line->numPoints();
597 if ( totalPoints < 2 )
598 return;
599
600 const double *x = line->xData();
601 const double *y = line->yData();
602 const double *z = line->is3D() ? line->zData() : nullptr;
603 const double *m = line->isMeasure() ? line->mData() : nullptr;
604
605 const double *xPainterUnits = linePainterUnits->xData();
606 const double *yPainterUnits = linePainterUnits->yData();
607
608 double prevX = *x++;
609 double prevY = *y++;
610 double prevZ = z ? *z++ : 0.0;
611 double prevM = m ? *m++ : 0.0;
612
613 double prevXPainterUnits = *xPainterUnits++;
614 double prevYPainterUnits = *yPainterUnits++;
615
616 if ( qgsDoubleNear( step, 0.0 ) )
617 {
618 visitPoint( prevX, prevY, prevZ, prevM, 0, 0 );
619 return;
620 }
621
622 double prevValue = useZ ? prevZ : prevM;
623 bool isFirstPoint = true;
624 for ( int i = 1; i < totalPoints; ++i )
625 {
626 double thisX = *x++;
627 double thisY = *y++;
628 double thisZ = z ? *z++ : 0.0;
629 double thisM = m ? *m++ : 0.0;
630 const double thisValue = useZ ? thisZ : thisM;
631 double thisXPainterUnits = *xPainterUnits++;
632 double thisYPainterUnits = *yPainterUnits++;
633
634 double angle = std::fmod( QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 );
635 if ( angle > 90 && angle < 270 )
636 angle += 180;
637
638 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
639 const double segmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( thisXPainterUnits, thisYPainterUnits, prevXPainterUnits, prevYPainterUnits );
640
641 // direction for this segment
642 const int direction = ( thisValue > prevValue ) ? 1 : ( thisValue < prevValue ) ? -1 : 0;
643 if ( direction != 0 )
644 {
645 // non-constant segment
646 double nextStepValue = direction > 0 ? std::ceil( prevValue / step ) * step : std::floor( prevValue / step ) * step;
647
648 while ( ( direction > 0 && ( nextStepValue <= thisValue || qgsDoubleNear( nextStepValue, thisValue ) ) )
649 || ( direction < 0 && ( nextStepValue >= thisValue || qgsDoubleNear( nextStepValue, thisValue ) ) ) )
650 {
651 const double targetPointFractionAlongSegment = ( nextStepValue - prevValue ) / ( thisValue - prevValue );
652 const double targetPointDistanceAlongSegment = targetPointFractionAlongSegment * segmentLengthPainterUnits;
653
654 double pX, pY;
655 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, targetPointFractionAlongSegment * segmentLength, pX, pY );
656
657 const double pZ = useZ ? nextStepValue : interpolateValue( prevZ, thisZ, targetPointFractionAlongSegment );
658 const double pM = useZ ? interpolateValue( prevM, thisM, targetPointFractionAlongSegment ) : nextStepValue;
659
660 double calculatedAngle = angle;
661 if ( averageAngleLengthPainterUnits > 0 )
662 {
663 calculatedAngle
664 = calculateAveragedAngle( targetPointDistanceAlongSegment, segmentLengthPainterUnits, averageAngleLengthPainterUnits, prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits, xPainterUnits, yPainterUnits, totalPoints, i );
665 }
666
667 if ( !qgsDoubleNear( targetPointFractionAlongSegment, 0 ) || isFirstPoint )
668 {
669 if ( !visitPoint( pX, pY, pZ, pM, distanceTraversed + segmentLength * targetPointFractionAlongSegment, calculatedAngle ) )
670 return;
671 }
672
673 nextStepValue += direction * step;
674 }
675 }
676 else if ( isFirstPoint && emitFirstPoint )
677 {
678 if ( !visitPoint( prevX, prevY, prevZ, prevM, distanceTraversed, std::fmod( QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 ) ) )
679 return;
680 }
681 isFirstPoint = false;
682
683 prevX = thisX;
684 prevY = thisY;
685 prevZ = thisZ;
686 prevM = thisM;
687 prevXPainterUnits = thisXPainterUnits;
688 prevYPainterUnits = thisYPainterUnits;
689 prevValue = thisValue;
690 distanceTraversed += segmentLength;
691 }
692}
693
695 const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint
696)
697{
698 visitPointsByInterpolatedZM( line, linePainterUnits, emitFirstPoint, distance, averageAngleLengthPainterUnits, visitPoint, true );
699}
700
702 const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint
703)
704{
705 visitPointsByInterpolatedZM( line, linePainterUnits, emitFirstPoint, distance, averageAngleLengthPainterUnits, visitPoint, false );
706}
707
708QPointF QgsLinearReferencingSymbolLayer::pointToPainter( QgsSymbolRenderContext &context, double x, double y, double z )
709{
710 QPointF pt;
711 if ( context.renderContext().coordinateTransform().isValid() )
712 {
714 pt = QPointF( x, y );
715 }
716 else
717 {
718 pt = QPointF( x, y );
719 }
720
721 context.renderContext().mapToPixel().transformInPlace( pt.rx(), pt.ry() );
722 return pt;
723}
724
725void QgsLinearReferencingSymbolLayer::renderPolylineInterval(
726 const QgsLineString *line, QgsSymbolRenderContext &context, double skipMultiples, const QPointF &labelOffsetPainterUnits, double averageAngleLengthPainterUnits, bool showMarker
727)
728{
729 double distance = mInterval;
731 {
732 context.setOriginalValueVariable( mInterval );
733 distance = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Interval, context.renderContext().expressionContext(), mInterval );
734 }
735
736 QgsNumericFormatContext numericContext;
737 numericContext.setExpressionContext( context.renderContext().expressionContext() );
738
739 std::unique_ptr< QgsLineString > painterUnitsGeometry( line->clone() );
740 if ( context.renderContext().coordinateTransform().isValid() )
741 {
742 painterUnitsGeometry->transform( context.renderContext().coordinateTransform() );
743 }
744 painterUnitsGeometry->transform( context.renderContext().mapToPixel().transform() );
745
746 const bool hasZ = line->is3D();
747 const bool hasM = line->isMeasure();
748 const bool emitFirstPoint = mLabelSource != Qgis::LinearReferencingLabelSource::CartesianDistance2D;
749
750 VisitPointAtDistanceFunction func = nullptr;
751
752 switch ( mPlacement )
753 {
756 break;
757
760 break;
761
764 break;
765
767 return;
768 }
769
770 QgsLinearReferencingSymbolLayerLabelProvider *labelProvider = nullptr;
771 if ( QgsLabelingEngine *labelingEngine = context.renderContext().labelingEngine() )
772 {
773 // rendering with a labeling engine. In this scenario we will register rendered text as labels, so that they participate in the labeling problem
774 // for the map
775 labelProvider = qgis::down_cast< QgsLinearReferencingSymbolLayerLabelProvider * >( labelingEngine->providerById( mLabelProviderId ) );
776 }
777
778 func(
779 line,
780 painterUnitsGeometry.get(),
781 emitFirstPoint,
782 distance,
783 averageAngleLengthPainterUnits,
784 [&context, &numericContext, skipMultiples, showMarker, labelOffsetPainterUnits, hasZ, hasM, labelProvider, this]( double x, double y, double z, double m, double distanceFromStart, double angle ) -> bool {
785 if ( context.renderContext().renderingStopped() )
786 return false;
787
788 double labelValue = 0;
789 bool labelVertex = true;
790 switch ( mLabelSource )
791 {
792 case Qgis::LinearReferencingLabelSource::CartesianDistance2D:
793 labelValue = distanceFromStart;
794 break;
795 case Qgis::LinearReferencingLabelSource::Z:
796 labelValue = z;
797 labelVertex = hasZ && !std::isnan( labelValue );
798 break;
799 case Qgis::LinearReferencingLabelSource::M:
800 labelValue = m;
801 labelVertex = hasM && !std::isnan( labelValue );
802 break;
803 }
804
805 if ( !labelVertex )
806 return true;
807
808 if ( skipMultiples > 0 && qgsDoubleNear( std::fmod( labelValue, skipMultiples ), 0 ) )
809 return true;
810
811 const QPointF pt = pointToPainter( context, x, y, z );
812
813 if ( mMarkerSymbol && showMarker )
814 {
815 if ( mRotateLabels )
816 mMarkerSymbol->setLineAngle( 90 - angle );
817 mMarkerSymbol->renderPoint( pt, context.feature(), context.renderContext() );
818 }
819
820 const double angleRadians = ( mRotateLabels ? angle : 0 ) * M_PI / 180.0;
821 const double dx = labelOffsetPainterUnits.x() * std::sin( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::sin( angleRadians );
822 const double dy = labelOffsetPainterUnits.x() * std::cos( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::cos( angleRadians );
823
824 const QString text = mNumericFormat->formatDouble( labelValue, numericContext );
825 if ( !labelProvider )
826 {
827 // render text directly
828 QgsTextRenderer::drawText( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, Qgis::TextHorizontalAlignment::Left, { text }, context.renderContext(), mTextFormat );
829 }
830 else
831 {
832 // register as a label
833 labelProvider->addLabel( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, text, context.renderContext(), mTextFormat );
834 }
835
836 return true;
837 }
838 );
839}
840
841void QgsLinearReferencingSymbolLayer::renderPolylineVertex(
842 const QgsLineString *line, QgsSymbolRenderContext &context, double skipMultiples, const QPointF &labelOffsetPainterUnits, double averageAngleLengthPainterUnits, bool showMarker
843)
844{
845 // let's simplify the logic by ALWAYS using the averaging approach for angles, and just
846 // use a very small distance if the user actually set this to 0. It'll be identical
847 // results anyway...
848 averageAngleLengthPainterUnits = std::max( averageAngleLengthPainterUnits, 0.1 );
849
850 QgsNumericFormatContext numericContext;
851 numericContext.setExpressionContext( context.renderContext().expressionContext() );
852
853 QgsLinearReferencingSymbolLayerLabelProvider *labelProvider = nullptr;
854 if ( QgsLabelingEngine *labelingEngine = context.renderContext().labelingEngine() )
855 {
856 // rendering with a labeling engine. In this scenario we will register rendered text as labels, so that they participate in the labeling problem
857 // for the map
858 labelProvider = qgis::down_cast< QgsLinearReferencingSymbolLayerLabelProvider * >( labelingEngine->providerById( mLabelProviderId ) );
859 }
860
861 const double *xData = line->xData();
862 const double *yData = line->yData();
863 const double *zData = line->is3D() ? line->zData() : nullptr;
864 const double *mData = line->isMeasure() ? line->mData() : nullptr;
865 const int size = line->numPoints();
866 if ( size < 2 )
867 return;
868
869 std::unique_ptr< QgsLineString > painterUnitsGeometry( line->clone() );
870 if ( context.renderContext().coordinateTransform().isValid() )
871 {
872 painterUnitsGeometry->transform( context.renderContext().coordinateTransform() );
873 }
874 painterUnitsGeometry->transform( context.renderContext().mapToPixel().transform() );
875 const double *xPainterUnits = painterUnitsGeometry->xData();
876 const double *yPainterUnits = painterUnitsGeometry->yData();
877
878 double currentDistance = 0;
879 double prevX = *xData;
880 double prevY = *yData;
881
882 for ( int i = 0; i < size; ++i )
883 {
884 if ( context.renderContext().renderingStopped() )
885 break;
886
887 double thisX = *xData++;
888 double thisY = *yData++;
889 double thisZ = zData ? *zData++ : 0;
890 double thisM = mData ? *mData++ : 0;
891 double thisXPainterUnits = *xPainterUnits++;
892 double thisYPainterUnits = *yPainterUnits++;
893
894 const double thisSegmentLength = QgsGeometryUtilsBase::distance2D( prevX, prevY, thisX, thisY );
895 currentDistance += thisSegmentLength;
896
897 if ( skipMultiples > 0 && qgsDoubleNear( std::fmod( currentDistance, skipMultiples ), 0 ) )
898 {
899 prevX = thisX;
900 prevY = thisY;
901 continue;
902 }
903
904 const QPointF pt = pointToPainter( context, thisX, thisY, thisZ );
905
906 // track forward by averageAngleLengthPainterUnits
907 double painterDistRemaining = averageAngleLengthPainterUnits;
908 double startAverageSegmentX = thisXPainterUnits;
909 double startAverageSegmentY = thisYPainterUnits;
910
911 const double *xAveragingData = xPainterUnits;
912 const double *yAveragingData = yPainterUnits;
913 double endAverageSegmentX = *xAveragingData;
914 double endAverageSegmentY = *yAveragingData;
915 double averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
916
917 int j = i;
918 while ( ( j < size - 1 ) && ( painterDistRemaining > averagingSegmentLengthPainterUnits ) )
919 {
920 painterDistRemaining -= averagingSegmentLengthPainterUnits;
921 startAverageSegmentX = endAverageSegmentX;
922 startAverageSegmentY = endAverageSegmentY;
923
924 endAverageSegmentX = *xAveragingData++;
925 endAverageSegmentY = *yAveragingData++;
926 j++;
927 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
928 }
929 // fits on this same segment
930 double endAverageXPainterUnits = thisXPainterUnits;
931 double endAverageYPainterUnits = thisYPainterUnits;
932 if ( ( j < size - 1 ) && painterDistRemaining < averagingSegmentLengthPainterUnits )
933 {
935 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, endAverageXPainterUnits, endAverageYPainterUnits, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr );
936 }
937 else if ( i < size - 2 )
938 {
939 endAverageXPainterUnits = endAverageSegmentX;
940 endAverageYPainterUnits = endAverageSegmentY;
941 }
942
943 // also track back by averageAngleLengthPainterUnits
944 j = i;
945 painterDistRemaining = averageAngleLengthPainterUnits;
946 startAverageSegmentX = thisXPainterUnits;
947 startAverageSegmentY = thisYPainterUnits;
948
949 xAveragingData = xPainterUnits - 2;
950 yAveragingData = yPainterUnits - 2;
951
952 endAverageSegmentX = *xAveragingData;
953 endAverageSegmentY = *yAveragingData;
954 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
955
956 while ( j > 0 && painterDistRemaining > averagingSegmentLengthPainterUnits )
957 {
958 painterDistRemaining -= averagingSegmentLengthPainterUnits;
959 startAverageSegmentX = endAverageSegmentX;
960 startAverageSegmentY = endAverageSegmentY;
961
962 endAverageSegmentX = *xAveragingData--;
963 endAverageSegmentY = *yAveragingData--;
964 j--;
965 averagingSegmentLengthPainterUnits = QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
966 }
967 // fits on this same segment
968 double startAverageXPainterUnits = thisXPainterUnits;
969 double startAverageYPainterUnits = thisYPainterUnits;
970 if ( j > 0 && painterDistRemaining < averagingSegmentLengthPainterUnits )
971 {
973 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, startAverageXPainterUnits, startAverageYPainterUnits, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr );
974 }
975 else if ( j > 1 )
976 {
977 startAverageXPainterUnits = endAverageSegmentX;
978 startAverageYPainterUnits = endAverageSegmentY;
979 }
980
981 double calculatedAngle = std::fmod( QgsGeometryUtilsBase::azimuth( startAverageXPainterUnits, startAverageYPainterUnits, endAverageXPainterUnits, endAverageYPainterUnits ) + 360, 360 );
982
983 if ( calculatedAngle > 90 && calculatedAngle < 270 )
984 calculatedAngle += 180;
985
986 if ( mMarkerSymbol && showMarker )
987 {
988 if ( mRotateLabels )
989 mMarkerSymbol->setLineAngle( 90 - calculatedAngle );
990 mMarkerSymbol->renderPoint( pt, context.feature(), context.renderContext() );
991 }
992
993 const double angleRadians = mRotateLabels ? ( calculatedAngle * M_PI / 180.0 ) : 0;
994 const double dx = labelOffsetPainterUnits.x() * std::sin( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::sin( angleRadians );
995 const double dy = labelOffsetPainterUnits.x() * std::cos( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::cos( angleRadians );
996
997 double labelValue = 0;
998 bool labelVertex = true;
999 switch ( mLabelSource )
1000 {
1002 labelValue = currentDistance;
1003 break;
1005 labelValue = thisZ;
1006 labelVertex = static_cast< bool >( zData ) && !std::isnan( labelValue );
1007 break;
1009 labelValue = thisM;
1010 labelVertex = static_cast< bool >( mData ) && !std::isnan( labelValue );
1011 break;
1012 }
1013
1014 if ( !labelVertex )
1015 continue;
1016
1017 const QString text = mNumericFormat->formatDouble( labelValue, numericContext );
1018 if ( !labelProvider )
1019 {
1020 // render text directly
1021 QgsTextRenderer::drawText( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, Qgis::TextHorizontalAlignment::Left, { text }, context.renderContext(), mTextFormat );
1022 }
1023 else
1024 {
1025 // register as a label
1026 labelProvider->addLabel( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, text, context.renderContext(), mTextFormat );
1027 }
1028 prevX = thisX;
1029 prevY = thisY;
1030 }
1031}
1032
1034{
1035 return mTextFormat;
1036}
1037
1039{
1040 mTextFormat = format;
1041}
1042
1044{
1045 return mNumericFormat.get();
1046}
1047
1049{
1050 mNumericFormat.reset( format );
1051}
1052
1054{
1055 return mInterval;
1056}
1057
1059{
1060 mInterval = interval;
1061}
1062
1064{
1065 return mSkipMultiplesOf;
1066}
1067
1072
1074{
1075 return mShowMarker;
1076}
1077
1079{
1080 mShowMarker = show;
1081 if ( show && !mMarkerSymbol )
1082 {
1083 mMarkerSymbol = QgsMarkerSymbol::createSimple( {} );
1084 }
1085}
1086
1091
1096
1101
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition qgis.h:796
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
Definition qgis.h:797
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1234
@ DisableFeatureClipping
If present, indicates that features should never be clipped to the map extent during rendering.
Definition qgis.h:907
@ AffectsLabeling
If present, indicates that the symbol layer will participate in the map labeling problem.
Definition qgis.h:909
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:913
@ Point
Text at point of origin layout mode.
Definition qgis.h:3004
@ Horizontal
Horizontally oriented text.
Definition qgis.h:2988
LinearReferencingPlacement
Defines how/where the labels should be placed in a linear referencing symbol layer.
Definition qgis.h:3263
@ IntervalZ
Place labels at regular intervals, linearly interpolated using Z values.
Definition qgis.h:3265
@ Vertex
Place labels on every vertex in the line.
Definition qgis.h:3267
@ IntervalM
Place labels at regular intervals, linearly interpolated using M values.
Definition qgis.h:3266
@ IntervalCartesian2D
Place labels at regular intervals, using Cartesian distance calculations on a 2D plane.
Definition qgis.h:3264
LinearReferencingLabelSource
Defines what quantity to use for the labels shown in a linear referencing symbol layer.
Definition qgis.h:3277
@ CartesianDistance2D
Distance along line, calculated using Cartesian calculations on a 2D plane.
Definition qgis.h:3278
QFlags< SymbolRenderHint > SymbolRenderHints
Symbol render hints.
Definition qgis.h:803
@ Marker
Marker symbol.
Definition qgis.h:637
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.
An abstract interface class for label providers.
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.
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:254
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.
QgsLineSymbolLayer(const QgsLineSymbolLayer &other)=delete
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.
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 std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context to use when evaluating QgsExpressions.
Abstract base class for numeric formatters, which allow for formatting a numeric value for display.
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:168
A container for the context for various read/write operations on objects.
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 copyCommonProperties(QgsSymbolLayer *destLayer) const
Copies all common base class properties from this layer to another symbol layer.
@ SkipMultiples
Skip multiples of.
@ AverageAngleLength
Length to average symbol angles over.
@ Interval
Line marker interval.
QgsPropertyCollection mDataDefinedProperties
QgsSymbolLayer(const QgsSymbolLayer &other)
Encapsulates the context in which a symbol is being rendered.
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:227
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:296
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...
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.
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.
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).
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition qgsgeos.h:112
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:7176
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7157
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
T qgsgeometry_cast(QgsAbstractGeometry *geom)
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)