QGIS API Documentation 4.3.0-Master (0cfde48c85b)
Loading...
Searching...
No Matches
qgslayoutitemelevationprofile.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemelevationprofile.cpp
3 ---------------------------------
4 begin : January 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <memory>
21
22#include "qgscurve.h"
23#include "qgslayout.h"
27#include "qgslayoututils.h"
28#include "qgslinesymbol.h"
30#include "qgsmessagelog.h"
31#include "qgsplot.h"
32#include "qgsprofilerenderer.h"
33#include "qgsprofilerequest.h"
36#include "qgssymbollayerutils.h"
37#include "qgsterrainprovider.h"
38#include "qgsvectorlayer.h"
39
40#include <QString>
41#include <QTimer>
42
43#include "moc_qgslayoutitemelevationprofile.cpp"
44
45using namespace Qt::StringLiterals;
46
47#define CACHE_SIZE_LIMIT 5000
48
50class QgsLayoutItemElevationProfilePlot : public Qgs2DXyPlot
51{
52 public:
53 QgsLayoutItemElevationProfilePlot() {}
54
55 void setRenderer( QgsProfilePlotRenderer *renderer )
56 {
57 // cppcheck-suppress danglingLifetime
58 mRenderer = renderer;
59 }
60
61 void renderContent( QgsRenderContext &rc, QgsPlotRenderContext &, const QRectF &plotArea, const QgsPlotData & ) override
62 {
63 if ( mRenderer )
64 {
65 const double distanceMin = xMinimum() * xScale;
66 const double distanceMax = xMaximum() * xScale;
67 rc.painter()->translate( plotArea.left(), plotArea.top() );
68 mRenderer->render( rc, plotArea.width(), plotArea.height(), distanceMin, distanceMax, yMinimum(), yMaximum() );
69 rc.painter()->translate( -plotArea.left(), -plotArea.top() );
70 mRenderer->renderSubsectionsIndicator( rc, plotArea, distanceMin, distanceMax, yMinimum(), yMaximum() );
71 }
72 }
73
74 double xScale = 1;
75
76 private:
77 QgsProfilePlotRenderer *mRenderer = nullptr;
78};
80
83 , mPlot( std::make_unique< QgsLayoutItemElevationProfilePlot >() )
84{
85 mBackgroundUpdateTimer = new QTimer( this );
86 mBackgroundUpdateTimer->setSingleShot( true );
87 connect( mBackgroundUpdateTimer, &QTimer::timeout, this, &QgsLayoutItemElevationProfile::recreateCachedImageInBackground );
88
89 setCacheMode( QGraphicsItem::NoCache );
90
91 if ( mLayout )
92 {
94 }
95
96 connect( this, &QgsLayoutItem::sizePositionChanged, this, [this] { invalidateCache(); } );
97
98 //default to no background
99 setBackgroundEnabled( false );
100
101 connect( QgsApplication::profileSourceRegistry(), &QgsProfileSourceRegistry::profileSourceRegistered, this, &QgsLayoutItemElevationProfile::setSourcesPrivate );
102 connect( QgsApplication::profileSourceRegistry(), &QgsProfileSourceRegistry::profileSourceUnregistered, this, &QgsLayoutItemElevationProfile::setSourcesPrivate );
103
104 setSourcesPrivate(); // Initialize with registered sources
105}
106
108{
109 if ( mRenderJob )
110 {
111 disconnect( mRenderJob.get(), &QgsProfilePlotRenderer::generationFinished, this, &QgsLayoutItemElevationProfile::profileGenerationFinished );
113 mRenderJob->cancelGeneration(); // blocks
114 mPainter->end();
115 }
116}
117
122
127
129{
130 return QgsApplication::getThemeIcon( u"mLayoutItemElevationProfile.svg"_s );
131}
132
134{
136
137 bool forceUpdate = false;
138
141 {
142 double value = mTolerance;
143
144 bool ok = false;
146
147 if ( !ok )
148 {
149 QgsMessageLog::logMessage( tr( "Elevation profile tolerance expression eval error" ) );
150 }
151 else
152 {
153 mTolerance = value;
154 }
155
156 forceUpdate = true;
157 }
158
161 {
162 double value = mPlot->xMinimum();
163
164 bool ok = false;
166
167 if ( !ok )
168 {
169 QgsMessageLog::logMessage( tr( "Elevation profile minimum distance expression eval error" ) );
170 }
171 else
172 {
173 mPlot->setXMinimum( value );
174 }
175
176 forceUpdate = true;
177 }
178
181 {
182 double value = mPlot->xMaximum();
183
184 bool ok = false;
186
187 if ( !ok )
188 {
189 QgsMessageLog::logMessage( tr( "Elevation profile maximum distance expression eval error" ) );
190 }
191 else
192 {
193 mPlot->setXMaximum( value );
194 }
195
196 forceUpdate = true;
197 }
198
201 {
202 double value = mPlot->yMinimum();
203
204 bool ok = false;
206
207 if ( !ok )
208 {
209 QgsMessageLog::logMessage( tr( "Elevation profile minimum elevation expression eval error" ) );
210 }
211 else
212 {
213 mPlot->setYMinimum( value );
214 }
215
216 forceUpdate = true;
217 }
218
221 {
222 double value = mPlot->yMaximum();
223
224 bool ok = false;
226
227 if ( !ok )
228 {
229 QgsMessageLog::logMessage( tr( "Elevation profile maximum elevation expression eval error" ) );
230 }
231 else
232 {
233 mPlot->setYMaximum( value );
234 }
235
236 forceUpdate = true;
237 }
238
241 {
242 double value = mPlot->xAxis().gridIntervalMajor();
243
244 bool ok = false;
246
247 if ( !ok )
248 {
249 QgsMessageLog::logMessage( tr( "Elevation profile distance axis major interval expression eval error" ) );
250 }
251 else
252 {
253 mPlot->xAxis().setGridIntervalMajor( value );
254 }
255
256 forceUpdate = true;
257 }
258
261 {
262 double value = mPlot->xAxis().gridIntervalMinor();
263
264 bool ok = false;
266
267 if ( !ok )
268 {
269 QgsMessageLog::logMessage( tr( "Elevation profile distance axis minor interval expression eval error" ) );
270 }
271 else
272 {
273 mPlot->xAxis().setGridIntervalMinor( value );
274 }
275
276 forceUpdate = true;
277 }
278
281 {
282 double value = mPlot->xAxis().labelInterval();
283
284 bool ok = false;
286
287 if ( !ok )
288 {
289 QgsMessageLog::logMessage( tr( "Elevation profile distance axis label interval expression eval error" ) );
290 }
291 else
292 {
293 mPlot->xAxis().setLabelInterval( value );
294 }
295
296 forceUpdate = true;
297 }
298
301 {
302 double value = mPlot->yAxis().gridIntervalMajor();
303
304 bool ok = false;
306
307 if ( !ok )
308 {
309 QgsMessageLog::logMessage( tr( "Elevation profile elevation axis major interval expression eval error" ) );
310 }
311 else
312 {
313 mPlot->yAxis().setGridIntervalMajor( value );
314 }
315
316 forceUpdate = true;
317 }
318
321 {
322 double value = mPlot->yAxis().gridIntervalMinor();
323
324 bool ok = false;
326
327 if ( !ok )
328 {
329 QgsMessageLog::logMessage( tr( "Elevation profile elevation axis minor interval expression eval error" ) );
330 }
331 else
332 {
333 mPlot->yAxis().setGridIntervalMinor( value );
334 }
335
336 forceUpdate = true;
337 }
338
341 {
342 double value = mPlot->yAxis().labelInterval();
343
344 bool ok = false;
346
347 if ( !ok )
348 {
349 QgsMessageLog::logMessage( tr( "Elevation profile elevation axis label interval expression eval error" ) );
350 }
351 else
352 {
353 mPlot->yAxis().setLabelInterval( value );
354 }
355
356 forceUpdate = true;
357 }
358
361 {
362 double value = mPlot->margins().left();
363
364 bool ok = false;
365 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginLeft, context, value, &ok );
366
367 if ( !ok )
368 {
369 QgsMessageLog::logMessage( tr( "Elevation profile left margin expression eval error" ) );
370 }
371 else
372 {
373 QgsMargins margins = mPlot->margins();
374 margins.setLeft( value );
375 mPlot->setMargins( margins );
376 }
377
378 forceUpdate = true;
379 }
380
383 {
384 double value = mPlot->margins().right();
385
386 bool ok = false;
387 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginRight, context, value, &ok );
388
389 if ( !ok )
390 {
391 QgsMessageLog::logMessage( tr( "Elevation profile right margin expression eval error" ) );
392 }
393 else
394 {
395 QgsMargins margins = mPlot->margins();
396 margins.setRight( value );
397 mPlot->setMargins( margins );
398 }
399
400 forceUpdate = true;
401 }
402
405 {
406 double value = mPlot->margins().top();
407
408 bool ok = false;
409 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginTop, context, value, &ok );
410
411 if ( !ok )
412 {
413 QgsMessageLog::logMessage( tr( "Elevation profile top margin expression eval error" ) );
414 }
415 else
416 {
417 QgsMargins margins = mPlot->margins();
418 margins.setTop( value );
419 mPlot->setMargins( margins );
420 }
421
422 forceUpdate = true;
423 }
424
427 {
428 double value = mPlot->margins().bottom();
429
430 bool ok = false;
431 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginBottom, context, value, &ok );
432
433 if ( !ok )
434 {
435 QgsMessageLog::logMessage( tr( "Elevation profile bottom margin expression eval error" ) );
436 }
437 else
438 {
439 QgsMargins margins = mPlot->margins();
440 margins.setBottom( value );
441 mPlot->setMargins( margins );
442 }
443
444 forceUpdate = true;
445 }
446
447 if ( forceUpdate )
448 {
449 mCacheInvalidated = true;
450
452 update();
453 }
454
456}
457
462
464{
465 return blendMode() != QPainter::CompositionMode_SourceOver;
466}
467
469{
470 return mEvaluatedOpacity < 1.0;
471}
472
474{
475 return mPlot.get();
476}
477
479{
480 return mPlot.get();
481}
482
483QList<QgsMapLayer *> QgsLayoutItemElevationProfile::layers() const
484{
485 return _qgis_listRefToRaw( mLayers );
486}
487
488void QgsLayoutItemElevationProfile::setLayers( const QList<QgsMapLayer *> &layers )
489{
490 if ( layers == _qgis_listRefToRaw( mLayers ) )
491 return;
492
493 mLayers = _qgis_listRawToRef( layers );
495}
496
497QList<QgsAbstractProfileSource *> QgsLayoutItemElevationProfile::sources() const
498{
499 if ( mSources.isEmpty() && !mLayers.isEmpty() )
500 {
501 // Legacy: If we have layers, extract their sources and return them.
502 // We don't set mSources here, because we want the previous check to
503 // continue failing if only layers are set.
504 // TODO: Remove this if block in QGIS 5.0 along with setLayers/layers methods.
505 QList< QgsAbstractProfileSource * > sources;
506 const QList<QgsMapLayer *> layersToGenerate = layers();
507 sources.reserve( layersToGenerate.size() );
508
509 for ( QgsMapLayer *layer : layersToGenerate )
510 {
511 if ( QgsAbstractProfileSource *source = layer->profileSource() )
512 sources << source;
513 }
514
515 // More legacy: elevation profile layers are in opposite direction
516 // to what the layer tree requires, and are in the same direction as
517 // the renderer expects, but since we reverse sources() (i.e., layers)
518 // before passing them to the renderer, then we need to reverse here first.
519 std::reverse( sources.begin(), sources.end() );
520
521 return sources;
522 }
523
524 QList< QgsAbstractProfileSource * > sources;
525 sources.reserve( mSources.count() );
526 for ( const auto &source : mSources )
527 {
528 if ( const QgsMapLayerRef *layerRef = std::get_if< QgsMapLayerRef >( &source ) )
529 {
530 if ( QgsMapLayer *layer = layerRef->get() )
531 {
532 sources << layer->profileSource();
533 }
534 }
535 else if ( auto profileSource = std::get_if< QgsAbstractProfileSource * >( &source ) )
536 {
537 if ( QgsApplication::profileSourceRegistry()->findSourceById( ( *profileSource )->profileSourceId() ) )
538 {
539 sources << *profileSource;
540 }
541 }
542 }
543
544 return sources;
545}
546
547void QgsLayoutItemElevationProfile::setSources( const QList<QgsAbstractProfileSource *> &sources )
548{
549 mSources.clear();
550 mSources.reserve( sources.count() );
551 for ( QgsAbstractProfileSource *profileSource : sources )
552 {
553 if ( auto layer = dynamic_cast<QgsMapLayer *>( profileSource ) )
554 {
555 mSources << QgsMapLayerRef( layer );
556 }
557 else if ( QgsApplication::profileSourceRegistry()->findSourceById( profileSource->profileSourceId() ) )
558 {
559 mSources << profileSource;
560 }
561 }
562
564}
565
567{
568 mCurve.reset( curve );
570}
571
573{
574 return mCurve.get();
575}
576
578{
579 if ( mCrs == crs )
580 return;
581
582 mCrs = crs;
584}
585
590
592{
593 if ( mTolerance == tolerance )
594 return;
595
596 mTolerance = tolerance;
598}
599
601{
602 return mTolerance;
603}
604
606{
607 mAtlasDriven = enabled;
608}
609
611{
612 QgsProfileRequest req( mCurve ? mCurve.get()->clone() : nullptr );
613
614 req.setCrs( mCrs );
615 req.setTolerance( mTolerance );
617 if ( mLayout )
618 {
619 if ( QgsProject *project = mLayout->project() )
620 {
621 req.setTransformContext( project->transformContext() );
622 if ( QgsAbstractTerrainProvider *provider = project->elevationProperties()->terrainProvider() )
623 {
624 req.setTerrainProvider( provider->clone() );
625 }
626 }
627 }
628 return req;
629}
630
631void QgsLayoutItemElevationProfile::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget * )
632{
633 if ( !mLayout || !painter || !painter->device() || !mUpdatesEnabled )
634 {
635 return;
636 }
637 if ( !shouldDrawItem() )
638 {
639 return;
640 }
641
642 QRectF thisPaintRect = rect();
643 if ( qgsDoubleNear( thisPaintRect.width(), 0.0 ) || qgsDoubleNear( thisPaintRect.height(), 0 ) )
644 return;
645
647 calculateAxisRanges( rc );
648
649 if ( mLayout->renderContext().isPreviewRender() )
650 {
652
653 QgsScopedQPainterState painterState( painter );
654 painter->setClipRect( thisPaintRect );
655 if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
656 {
657 // No initial render available - so draw some preview text alerting user
658 painter->setBrush( QBrush( QColor( 125, 125, 125, 125 ) ) );
659 painter->drawRect( thisPaintRect );
660 painter->setBrush( Qt::NoBrush );
661 QFont messageFont;
662 messageFont.setPointSize( 12 );
663 painter->setFont( messageFont );
664 painter->setPen( QColor( 255, 255, 255, 255 ) );
665 painter->drawText( thisPaintRect, Qt::AlignCenter | Qt::AlignHCenter, tr( "Rendering profile" ) );
666
667 if (
668 ( mRenderJob && mCacheInvalidated && !mDrawingPreview ) // current job was invalidated - start a new one
669 || ( !mRenderJob && !mDrawingPreview ) // this is the profiles's very first paint - trigger a cache update
670 )
671 {
672 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
673 mBackgroundUpdateTimer->start( 1 );
674 }
675 }
676 else
677 {
678 if ( mCacheInvalidated && !mDrawingPreview )
679 {
680 // cache was invalidated - trigger a background update
681 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
682 mBackgroundUpdateTimer->start( 1 );
683 }
684
685 //Background color is already included in cached image, so no need to draw
686
687 double imagePixelWidth = mCacheFinalImage->width(); //how many pixels of the image are for the map extent?
688 double scale = rect().width() / imagePixelWidth;
689
690 QgsScopedQPainterState rotatedPainterState( painter );
691
692 painter->scale( scale, scale );
693 painter->setCompositionMode( blendModeForRender() );
694 painter->drawImage( 0, 0, *mCacheFinalImage );
695 }
696
697 painter->setClipRect( thisPaintRect, Qt::NoClip );
698
699 if ( frameEnabled() )
700 {
702 }
703 }
704 else
705 {
706 if ( mDrawing )
707 return;
708
709 mDrawing = true;
710 QPaintDevice *paintDevice = painter->device();
711 if ( !paintDevice )
712 return;
713
714 QSizeF layoutSize = mLayout->convertToLayoutUnits( sizeWithUnits() );
715
716 if ( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::LosslessImageRendering )
717 painter->setRenderHint( QPainter::LosslessImageRendering, true );
718
719 mPlot->xScale = QgsUnitTypes::fromUnitToUnitFactor( mDistanceUnit, mCrs.mapUnits() );
720
721 if ( !qgsDoubleNear( layoutSize.width(), 0.0 ) && !qgsDoubleNear( layoutSize.height(), 0.0 ) )
722 {
723 const bool forceVector = mLayout && mLayout->renderContext().rasterizedRenderingPolicy() == Qgis::RasterizedRenderingPolicy::ForceVector;
724 if ( ( containsAdvancedEffects() || ( blendModeForRender() != QPainter::CompositionMode_SourceOver ) ) && !forceVector )
725 {
726 // rasterize
727 double destinationDpi = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter ) * 25.4;
728 double layoutUnitsInInches = mLayout ? mLayout->convertFromLayoutUnits( 1, Qgis::LayoutUnit::Inches ).length() : 1;
729 int widthInPixels = static_cast< int >( std::round( boundingRect().width() * layoutUnitsInInches * destinationDpi ) );
730 int heightInPixels = static_cast< int >( std::round( boundingRect().height() * layoutUnitsInInches * destinationDpi ) );
731 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
732
733 image.fill( Qt::transparent );
734 image.setDotsPerMeterX( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
735 image.setDotsPerMeterY( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
736 double dotsPerMM = destinationDpi / 25.4;
737 layoutSize *= dotsPerMM; // output size will be in dots (pixels)
738 QPainter p( &image );
739 preparePainter( &p );
740
743
744 p.scale( dotsPerMM, dotsPerMM );
745 if ( hasBackground() )
746 {
748 }
749
750 p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
751
752 const double mapUnitsPerPixel = static_cast<double>( mPlot->xMaximum() - mPlot->xMinimum() ) * mPlot->xScale / layoutSize.width();
753 rc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
754
755 QList< QgsAbstractProfileSource *> sourcesToRender = sources();
756 std::reverse( sourcesToRender.begin(), sourcesToRender.end() ); // sources are rendered from bottom to top
757
758 QgsProfilePlotRenderer renderer( sourcesToRender, profileRequest() );
759 std::unique_ptr<QgsLineSymbol> rendererSubSectionsSymbol( subsectionsSymbol() ? subsectionsSymbol()->clone() : nullptr );
760 renderer.setSubsectionsSymbol( rendererSubSectionsSymbol.release() );
761
762 renderer.generateSynchronously();
763 mPlot->setRenderer( &renderer );
764
765 // size must be in pixels, not layout units
766 mPlot->setSize( layoutSize );
767
768 QgsPlotRenderContext plotContext;
769 mPlot->render( rc, plotContext );
770
771 mPlot->setRenderer( nullptr );
772
773 p.scale( dotsPerMM, dotsPerMM );
774
775 if ( frameEnabled() )
776 {
778 }
779
780 QgsScopedQPainterState painterState( painter );
781 painter->setCompositionMode( blendModeForRender() );
782 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
783 painter->drawImage( 0, 0, image );
784 painter->scale( dotsPerMM, dotsPerMM );
785 }
786 else
787 {
790
791 // Fill with background color
792 if ( hasBackground() )
793 {
795 }
796
797 QgsScopedQPainterState painterState( painter );
798 QgsScopedQPainterState stagedPainterState( painter );
799 double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
800 layoutSize *= dotsPerMM; // output size will be in dots (pixels)
801 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
802
803 const double mapUnitsPerPixel = static_cast<double>( mPlot->xMaximum() - mPlot->xMinimum() ) * mPlot->xScale / layoutSize.width();
804 rc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
805
806 QList< QgsAbstractProfileSource *> sourcesToRender = sources();
807 std::reverse( sourcesToRender.begin(), sourcesToRender.end() ); // sources are rendered from bottom to top
808
809 QgsProfilePlotRenderer renderer( sourcesToRender, profileRequest() );
810 std::unique_ptr<QgsLineSymbol> rendererSubSectionsSymbol( subsectionsSymbol() ? subsectionsSymbol()->clone() : nullptr );
811 renderer.setSubsectionsSymbol( rendererSubSectionsSymbol.release() );
812
813 // TODO
814 // we should be able to call renderer.start()/renderer.waitForFinished() here and
815 // benefit from parallel source generation. BUT
816 // for some reason the QtConcurrent::map call in start() never triggers
817 // the actual background thread execution.
818 // So for now just generate the results one by one
819 renderer.generateSynchronously();
820 mPlot->setRenderer( &renderer );
821
822 // size must be in pixels, not layout units
823 mPlot->setSize( layoutSize );
824
825 QgsPlotRenderContext plotContext;
826 mPlot->render( rc, plotContext );
827
828 mPlot->setRenderer( nullptr );
829
830 painter->setClipRect( thisPaintRect, Qt::NoClip );
831
832 if ( frameEnabled() )
833 {
835 }
836 }
837 }
838
839 mDrawing = false;
840 }
841}
842
844{
845 if ( mAtlasDriven && mLayout && mLayout->reportContext().layer() )
846 {
847 if ( QgsVectorLayer *layer = mLayout->reportContext().layer() )
848 {
849 mCrs = layer->crs();
850 }
851 const QgsGeometry curveGeom( mLayout->reportContext().currentGeometry( mCrs ) );
852 if ( const QgsAbstractGeometry *geom = curveGeom.constGet() )
853 {
854 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom->simplifiedTypeRef() ) )
855 {
856 mCurve.reset( curve->clone() );
857 }
858 }
859 }
860
863}
864
866{
867 if ( mDrawing )
868 return;
869
870 mCacheInvalidated = true;
871 update();
872}
873
876
877bool QgsLayoutItemElevationProfile::writePropertiesToElement( QDomElement &layoutProfileElem, QDomDocument &doc, const QgsReadWriteContext &rwContext ) const
878{
879 {
880 QDomElement plotElement = doc.createElement( u"plot"_s );
881 mPlot->writeXml( plotElement, doc, rwContext );
882 layoutProfileElem.appendChild( plotElement );
883 }
884
886 {
887 layoutProfileElem.setAttribute( u"rangeMethod"_s, qgsEnumValueToKey( mRangeMethod ) );
888 }
889 if ( !qgsDoubleNear( mDistanceScale, 1000 ) )
890 {
891 layoutProfileElem.setAttribute( u"distanceScale"_s, qgsDoubleToString( mDistanceScale ) );
892 }
893 if ( !qgsDoubleNear( mElevationScale, 10 ) )
894 {
895 layoutProfileElem.setAttribute( u"elevationScale"_s, qgsDoubleToString( mElevationScale ) );
896 }
897
898 layoutProfileElem.setAttribute( u"distanceUnit"_s, qgsEnumValueToKey( mDistanceUnit ) );
899
900 layoutProfileElem.setAttribute( u"tolerance"_s, mTolerance );
901 layoutProfileElem.setAttribute( u"atlasDriven"_s, mAtlasDriven ? u"1"_s : u"0"_s );
902 if ( mCrs.isValid() )
903 {
904 QDomElement crsElem = doc.createElement( u"crs"_s );
905 mCrs.writeXml( crsElem, doc );
906 layoutProfileElem.appendChild( crsElem );
907 }
908 if ( mCurve )
909 {
910 QDomElement curveElem = doc.createElement( u"curve"_s );
911 curveElem.appendChild( doc.createTextNode( mCurve->asWkt() ) );
912 layoutProfileElem.appendChild( curveElem );
913 }
914
915 {
916 QDomElement layersElement = doc.createElement( u"layers"_s );
917 for ( const QgsMapLayerRef &layer : mLayers )
918 {
919 QDomElement layerElement = doc.createElement( u"layer"_s );
920 layer.writeXml( layerElement, rwContext );
921 layersElement.appendChild( layerElement );
922 }
923 layoutProfileElem.appendChild( layersElement );
924 }
925
926 {
927 QDomElement sourcesElement = doc.createElement( u"profileSources"_s );
928 for ( const auto &source : mSources )
929 {
930 if ( const QgsMapLayerRef *layerRef = std::get_if< QgsMapLayerRef >( &source ) )
931 {
932 if ( layerRef->get() )
933 {
934 QDomElement sourceElement = doc.createElement( u"profileLayerSource"_s );
935 layerRef->writeXml( sourceElement, rwContext );
936 sourcesElement.appendChild( sourceElement );
937 }
938 }
939 else if ( auto profileSource = std::get_if< QgsAbstractProfileSource * >( &source ) )
940 {
941 if ( QgsApplication::profileSourceRegistry()->findSourceById( ( *profileSource )->profileSourceId() ) )
942 {
943 QDomElement sourceElement = doc.createElement( u"profileCustomSource"_s );
944 sourceElement.setAttribute( u"id"_s, ( *profileSource )->profileSourceId() );
945 sourcesElement.appendChild( sourceElement );
946 }
947 }
948 }
949
950 layoutProfileElem.appendChild( sourcesElement );
951 }
952
953 if ( mSubsectionsSymbol )
954 {
955 QDomElement subsectionsElement = doc.createElement( u"subsections"_s );
956 const QDomElement symbolElement = QgsSymbolLayerUtils::saveSymbol( u"subsections"_s, mSubsectionsSymbol.get(), doc, rwContext );
957 subsectionsElement.appendChild( symbolElement );
958 layoutProfileElem.appendChild( subsectionsElement );
959 }
960
961 return true;
962}
963
964bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext &context )
965{
966 const QDomElement plotElement = itemElem.firstChildElement( u"plot"_s );
967 if ( !plotElement.isNull() )
968 {
969 mPlot->readXml( plotElement, context );
970 }
971
972 const QDomNodeList crsNodeList = itemElem.elementsByTagName( u"crs"_s );
974 if ( !crsNodeList.isEmpty() )
975 {
976 const QDomElement crsElem = crsNodeList.at( 0 ).toElement();
977 crs.readXml( crsElem );
978 }
979 mCrs = crs;
980
981 mRangeMethod = qgsEnumKeyToValue( itemElem.attribute( u"rangeMethod"_s ), Qgis::ElevationProfileRangeMethod::ManualRange );
982 mDistanceScale = itemElem.attribute( u"distanceScale"_s ).toDouble();
983 mElevationScale = itemElem.attribute( u"elevationScale"_s ).toDouble();
984
985 setDistanceUnit( qgsEnumKeyToValue( itemElem.attribute( u"distanceUnit"_s ), mCrs.mapUnits() ) );
986
987 const QDomNodeList curveNodeList = itemElem.elementsByTagName( u"curve"_s );
988 if ( !curveNodeList.isEmpty() )
989 {
990 const QDomElement curveElem = curveNodeList.at( 0 ).toElement();
991 const QgsGeometry curve = QgsGeometry::fromWkt( curveElem.text() );
992 if ( const QgsCurve *curveGeom = qgsgeometry_cast< const QgsCurve * >( curve.constGet() ) )
993 {
994 mCurve.reset( curveGeom->clone() );
995 }
996 else
997 {
998 mCurve.reset();
999 }
1000 }
1001
1002 mTolerance = itemElem.attribute( u"tolerance"_s ).toDouble();
1003 mAtlasDriven = static_cast< bool >( itemElem.attribute( u"atlasDriven"_s, u"0"_s ).toInt() );
1004
1005 {
1006 mLayers.clear();
1007 const QDomElement layersElement = itemElem.firstChildElement( u"layers"_s );
1008 QDomElement layerElement = layersElement.firstChildElement( u"layer"_s );
1009 while ( !layerElement.isNull() )
1010 {
1011 QgsMapLayerRef ref;
1012 ref.readXml( layerElement, context );
1013 ref.resolveWeakly( mLayout->project() );
1014 mLayers.append( ref );
1015
1016 layerElement = layerElement.nextSiblingElement( u"layer"_s );
1017 }
1018 }
1019
1020 {
1021 mSources.clear();
1022 mSources.reserve( itemElem.elementsByTagName( u"profileSources"_s ).count() );
1023 const QDomElement sourcesElement = itemElem.firstChildElement( u"profileSources"_s );
1024 QDomElement sourceElement = sourcesElement.firstChildElement();
1025 while ( !sourceElement.isNull() )
1026 {
1027 if ( sourceElement.tagName() == "profileCustomSource"_L1 )
1028 {
1029 const QString sourceId = sourceElement.attribute( u"id"_s );
1030 if ( QgsAbstractProfileSource *profileSource = QgsApplication::profileSourceRegistry()->findSourceById( sourceId ) )
1031 {
1032 mSources << profileSource;
1033 }
1034 }
1035 else if ( sourceElement.tagName() == "profileLayerSource"_L1 )
1036 {
1037 QgsMapLayerRef ref;
1038 ref.readXml( sourceElement, context );
1039 ref.resolveWeakly( mLayout->project() );
1040 if ( ref.get() )
1041 {
1042 mSources << ref;
1043 }
1044 }
1045
1046 sourceElement = sourceElement.nextSiblingElement();
1047 }
1048 }
1049
1050 const QDomElement subsectionsElement = itemElem.firstChildElement( u"subsections"_s );
1051 const QDomElement symbolsElement = subsectionsElement.firstChildElement( u"symbol"_s );
1052 if ( !symbolsElement.isNull() )
1053 {
1054 std::unique_ptr< QgsLineSymbol > subSectionsSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol >( symbolsElement, context );
1055 if ( subSectionsSymbol )
1056 {
1057 setSubsectionsSymbol( subSectionsSymbol.release() );
1058 }
1059 }
1060
1061 return true;
1062}
1063
1064void QgsLayoutItemElevationProfile::recreateCachedImageInBackground()
1065{
1066 if ( mRenderJob )
1067 {
1068 disconnect( mRenderJob.get(), &QgsProfilePlotRenderer::generationFinished, this, &QgsLayoutItemElevationProfile::profileGenerationFinished );
1069 QgsProfilePlotRenderer *oldJob = mRenderJob.release();
1070 QPainter *oldPainter = mPainter.release();
1071 QImage *oldImage = mCacheRenderingImage.release();
1072 connect( oldJob, &QgsProfilePlotRenderer::generationFinished, this, [oldPainter, oldJob, oldImage] {
1073 oldJob->deleteLater();
1074 delete oldPainter;
1075 delete oldImage;
1076 } );
1078 }
1079 else
1080 {
1081 mCacheRenderingImage.reset( nullptr );
1083 }
1084
1085 Q_ASSERT( !mRenderJob );
1086 Q_ASSERT( !mPainter );
1087 Q_ASSERT( !mCacheRenderingImage );
1088
1089 const QSizeF layoutSize = mLayout->convertToLayoutUnits( sizeWithUnits() );
1090 double widthLayoutUnits = layoutSize.width();
1091 double heightLayoutUnits = layoutSize.height();
1092
1093 int w = static_cast< int >( std::round( widthLayoutUnits * mPreviewScaleFactor ) );
1094 int h = static_cast< int >( std::round( heightLayoutUnits * mPreviewScaleFactor ) );
1095
1096 // limit size of image for better performance
1097 if ( w > 5000 || h > 5000 )
1098 {
1099 if ( w > h )
1100 {
1101 w = 5000;
1102 h = static_cast< int>( std::round( w * heightLayoutUnits / widthLayoutUnits ) );
1103 }
1104 else
1105 {
1106 h = 5000;
1107 w = static_cast< int >( std::round( h * widthLayoutUnits / heightLayoutUnits ) );
1108 }
1109 }
1110
1111 if ( w <= 0 || h <= 0 )
1112 return;
1113
1114 mCacheRenderingImage = std::make_unique<QImage>( w, h, QImage::Format_ARGB32 );
1115
1116 // set DPI of the image
1117 mCacheRenderingImage->setDotsPerMeterX( static_cast< int >( std::round( 1000 * w / widthLayoutUnits ) ) );
1118 mCacheRenderingImage->setDotsPerMeterY( static_cast< int >( std::round( 1000 * h / heightLayoutUnits ) ) );
1119
1120 //start with empty fill to avoid artifacts
1121 mCacheRenderingImage->fill( Qt::transparent );
1122 if ( hasBackground() )
1123 {
1124 //Initially fill image with specified background color
1125 mCacheRenderingImage->fill( backgroundColor().rgba() );
1126 }
1127
1128 mCacheInvalidated = false;
1129 mPainter = std::make_unique<QPainter>( mCacheRenderingImage.get() );
1130
1131 QList< QgsAbstractProfileSource *> sourcesToRender = sources();
1132 std::reverse( sourcesToRender.begin(), sourcesToRender.end() ); // sources are rendered from bottom to top
1133
1134 mRenderJob = std::make_unique< QgsProfilePlotRenderer >( sourcesToRender, profileRequest() );
1135 std::unique_ptr<QgsLineSymbol> rendererSubSectionsSymbol( subsectionsSymbol() ? subsectionsSymbol()->clone() : nullptr );
1136 mRenderJob->setSubsectionsSymbol( rendererSubSectionsSymbol.release() );
1137 connect( mRenderJob.get(), &QgsProfilePlotRenderer::generationFinished, this, &QgsLayoutItemElevationProfile::profileGenerationFinished );
1138 mRenderJob->startGeneration();
1139
1140 mDrawingPreview = false;
1141}
1142
1143void QgsLayoutItemElevationProfile::profileGenerationFinished()
1144{
1145 mPlot->setRenderer( mRenderJob.get() );
1146
1147 QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( mLayout, mPainter.get() );
1148
1149 mPlot->xScale = QgsUnitTypes::fromUnitToUnitFactor( mDistanceUnit, mCrs.mapUnits() );
1150
1151 const double mapUnitsPerPixel = static_cast< double >( mPlot->xMaximum() - mPlot->xMinimum() ) * mPlot->xScale / mCacheRenderingImage->size().width();
1152 rc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
1153
1154 // size must be in pixels, not layout units
1155 mPlot->setSize( mCacheRenderingImage->size() );
1156
1157 QgsPlotRenderContext plotContext;
1158 mPlot->render( rc, plotContext );
1159
1160 mPlot->setRenderer( nullptr );
1161
1162 mPainter->end();
1163 mRenderJob.reset( nullptr );
1164 mPainter.reset( nullptr );
1165 mCacheFinalImage = std::move( mCacheRenderingImage );
1167 update();
1168 emit previewRefreshed();
1169}
1170
1172{
1173 return mDistanceUnit;
1174}
1175
1177{
1178 mDistanceUnit = unit;
1179
1180 switch ( mDistanceUnit )
1181 {
1230 mPlot->xAxis().setLabelSuffix( u" %1"_s.arg( QgsUnitTypes::toAbbreviatedString( mDistanceUnit ) ) );
1231 break;
1232
1234 mPlot->xAxis().setLabelSuffix( QObject::tr( "°" ) );
1235 break;
1236
1238 mPlot->xAxis().setLabelSuffix( QString() );
1239 break;
1240 }
1241}
1242
1244{
1245 mSubsectionsSymbol.reset( symbol );
1246}
1247
1248void QgsLayoutItemElevationProfile::setSourcesPrivate()
1249{
1250 mSources.clear();
1251 mSources.reserve( QgsApplication::profileSourceRegistry()->profileSources().count() );
1252 for ( QgsAbstractProfileSource *source : QgsApplication::profileSourceRegistry()->profileSources() )
1253 mSources << source;
1254}
1255
1256void QgsLayoutItemElevationProfile::calculateAxisRanges( QgsRenderContext &context )
1257{
1258 if ( !mLayout )
1259 return;
1260
1261 switch ( mRangeMethod )
1262 {
1264 return; // nothing to do - axis range can't change!
1265
1267 break;
1268 }
1269
1270 const double mmToPainterUnits = context.convertToPainterUnits( 1.0, Qgis::RenderUnit::Millimeters );
1271 if ( qgsDoubleNear( mmToPainterUnits, 0.0 ) )
1272 return;
1273
1274 const QSizeF layoutSize = mLayout->convertToLayoutUnits( sizeWithUnits() );
1275 QgsLayoutSize layoutSizeMm = mLayout->convertFromLayoutUnits( layoutSize, Qgis::LayoutUnit::Millimeters );
1276
1277 mPlot->setSize( layoutSizeMm.toQSizeF() * mmToPainterUnits );
1278
1279 QgsPlotRenderContext plotContext;
1280
1281 // We need to do an iterative approach here. First we set a rough range based on the total size of the item.
1282 // But this is NOT correct, because that total size also includes the area outside of the plot itself (ie axis labels etc).
1283 // So then we set that range, and grab the interiorPlotArea corresponding to the plot with those initial guess set of axis labels.
1284 // From there we iterate, using the interiorPlotArea each time until we converge on the desired scale.
1285
1286 // first guess - assuming the whole item is the interior (incorrect!)
1287 if ( mDistanceScale > 0 )
1288 {
1289 const double widthMeters = layoutSizeMm.width() / 1000;
1290 const double distanceRange = QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, mDistanceUnit ) * widthMeters * mDistanceScale;
1291 mPlot->setXMinimum( 0 );
1292 mPlot->setXMaximum( distanceRange );
1293 }
1294 if ( mElevationScale > 0 )
1295 {
1296 const double heightMeters = layoutSizeMm.height() / 1000;
1297 const double elevationRange = heightMeters * mElevationScale;
1298 mPlot->setYMaximum( mPlot->yMinimum() + elevationRange );
1299 }
1300
1301 // now we start the iteration
1302 QRectF previousPlotArea;
1303 for ( int iteration = 0; iteration < 5; ++iteration )
1304 {
1305 const QRectF currentPlotArea = mPlot->interiorPlotArea( context, plotContext );
1306 if ( qgsDoubleNear( currentPlotArea.width(), previousPlotArea.width(), 1 ) && qgsDoubleNear( currentPlotArea.height(), previousPlotArea.height(), 1 ) )
1307 break;
1308
1309 previousPlotArea = currentPlotArea;
1310
1311 if ( mDistanceScale > 0 )
1312 {
1313 const double widthMm = currentPlotArea.width() / mmToPainterUnits;
1314 const double widthMeters = widthMm / 1000.0;
1315 const double distanceRange = QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, mDistanceUnit ) * widthMeters * mDistanceScale;
1316 mPlot->setXMaximum( distanceRange );
1317 }
1318
1319 if ( mElevationScale > 0 )
1320 {
1321 const double heightMm = currentPlotArea.height() / mmToPainterUnits;
1322 const double heightMeters = heightMm / 1000.0;
1323 const double elevationRange = heightMeters * mElevationScale;
1324 mPlot->setYMaximum( mPlot->yMinimum() + elevationRange );
1325 }
1326 }
1327}
1328
1330{
1331 return mElevationScale;
1332}
1333
1335{
1336 mElevationScale = scale;
1337}
1338
1340{
1341 return mDistanceScale;
1342}
1343
1345{
1346 mDistanceScale = scale;
1347}
1348
1353
1355{
1356 mRangeMethod = method;
1357}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2919
@ Millimeters
Millimeters.
Definition qgis.h:5779
@ Inches
Inches.
Definition qgis.h:5782
DistanceUnit
Units of distance.
Definition qgis.h:5568
@ YardsBritishSears1922Truncated
British yards (Sears 1922 truncated).
Definition qgis.h:5608
@ Feet
Imperial feet.
Definition qgis.h:5571
@ MilesUSSurvey
US Survey miles.
Definition qgis.h:5615
@ LinksBritishSears1922
British links (Sears 1922).
Definition qgis.h:5603
@ YardsBritishBenoit1895A
British yards (Benoit 1895 A).
Definition qgis.h:5606
@ LinksBritishBenoit1895A
British links (Benoit 1895 A).
Definition qgis.h:5600
@ Centimeters
Centimeters.
Definition qgis.h:5576
@ YardsIndian1975
Indian yards (1975).
Definition qgis.h:5614
@ FeetUSSurvey
US Survey feet.
Definition qgis.h:5598
@ Millimeters
Millimeters.
Definition qgis.h:5577
@ FeetBritishSears1922
British feet (Sears 1922).
Definition qgis.h:5591
@ YardsClarkes
Clarke's yards.
Definition qgis.h:5610
@ YardsIndian
Indian yards.
Definition qgis.h:5611
@ FeetBritishBenoit1895B
British feet (Benoit 1895 B).
Definition qgis.h:5589
@ Miles
Terrestrial miles.
Definition qgis.h:5574
@ LinksUSSurvey
US Survey links.
Definition qgis.h:5605
@ Meters
Meters.
Definition qgis.h:5569
@ ChainsUSSurvey
US Survey chains.
Definition qgis.h:5585
@ FeetClarkes
Clarke's feet.
Definition qgis.h:5592
@ Unknown
Unknown distance unit.
Definition qgis.h:5618
@ Yards
Imperial yards.
Definition qgis.h:5573
@ FeetBritish1936
British feet (1936).
Definition qgis.h:5587
@ FeetIndian1962
Indian feet (1962).
Definition qgis.h:5596
@ YardsBritishSears1922
British yards (Sears 1922).
Definition qgis.h:5609
@ FeetIndian1937
Indian feet (1937).
Definition qgis.h:5595
@ YardsIndian1937
Indian yards (1937).
Definition qgis.h:5612
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5575
@ ChainsBritishBenoit1895B
British chains (Benoit 1895 B).
Definition qgis.h:5581
@ LinksBritishSears1922Truncated
British links (Sears 1922 truncated).
Definition qgis.h:5602
@ ChainsBritishBenoit1895A
British chains (Benoit 1895 A).
Definition qgis.h:5580
@ YardsBritishBenoit1895B
British yards (Benoit 1895 B).
Definition qgis.h:5607
@ FeetBritish1865
British feet (1865).
Definition qgis.h:5586
@ YardsIndian1962
Indian yards (1962).
Definition qgis.h:5613
@ FeetBritishSears1922Truncated
British feet (Sears 1922 truncated).
Definition qgis.h:5590
@ MetersGermanLegal
German legal meter.
Definition qgis.h:5617
@ LinksBritishBenoit1895B
British links (Benoit 1895 B).
Definition qgis.h:5601
@ ChainsInternational
International chains.
Definition qgis.h:5579
@ Inches
Inches.
Definition qgis.h:5578
@ Fathoms
Fathoms.
Definition qgis.h:5616
@ LinksInternational
International links.
Definition qgis.h:5599
@ ChainsBritishSears1922Truncated
British chains (Sears 1922 truncated).
Definition qgis.h:5582
@ FeetIndian
Indian (geodetic) feet.
Definition qgis.h:5594
@ NauticalMiles
Nautical miles.
Definition qgis.h:5572
@ ChainsClarkes
Clarke's chains.
Definition qgis.h:5584
@ LinksClarkes
Clarke's links.
Definition qgis.h:5604
@ ChainsBritishSears1922
British chains (Sears 1922).
Definition qgis.h:5583
@ Kilometers
Kilometers.
Definition qgis.h:5570
@ FeetIndian1975
Indian feet (1975).
Definition qgis.h:5597
@ FeetGoldCoast
Gold Coast feet.
Definition qgis.h:5593
@ FeetBritishBenoit1895A
British feet (Benoit 1895 A).
Definition qgis.h:5588
@ Millimeters
Millimeters.
Definition qgis.h:5759
ElevationProfileRangeMethod
Elevation profile range calculation methods.
Definition qgis.h:6165
@ ManualRange
Distance/elevation ranges are manually set.
Definition qgis.h:6166
@ FixedScale
Distance and elevation ranges are calculated from fixed scales.
Definition qgis.h:6167
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
Definition qgis.h:5825
virtual void renderContent(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QRectF &plotArea, const QgsPlotData &plotData=QgsPlotData())
Renders the plot content.
Definition qgsplot.cpp:310
Base class for 2-dimensional plot/chart/graphs with an X and Y axes.
Definition qgsplot.h:687
double yMaximum() const
Returns the maximum value of the y axis.
Definition qgsplot.h:769
double xMinimum() const
Returns the minimum value of the x axis.
Definition qgsplot.h:727
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:741
double xMaximum() const
Returns the maximum value of the x axis.
Definition qgsplot.h:755
Abstract base class for all geometries.
Interface for classes which can generate elevation profiles.
Abstract base class for terrain providers.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsProfileSourceRegistry * profileSourceRegistry()
Returns registry of available profile source implementations.
Represents a coordinate reference system (CRS).
Abstract base class for curved geometry type.
Definition qgscurve.h:36
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
static QgsLayoutItemElevationProfile * create(QgsLayout *layout)
Returns a new elevation profile item for the specified layout.
Qgs2DXyPlot * plot()
Returns a reference to the elevation plot object, which can be used to set plot appearance and proper...
void setElevationScale(double scale)
Sets the denominator of the elevation scale.
QgsLayoutItemElevationProfile(QgsLayout *layout)
Constructor for QgsLayoutItemElevationProfile, with the specified parent layout.
Qgis::ElevationProfileRangeMethod rangeMethod() const
Returns the method used to determine the elevation/distance ranges on the profile.
QgsCurve * profileCurve() const
Returns the cross section profile curve, which represents the line along which the profile should be ...
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of map layers participating in the elevation profile.
void setSubsectionsSymbol(QgsLineSymbol *symbol)
Sets the symbol used to draw the subsections.
QList< QgsMapLayer * > layers() const
Returns the list of map layers participating in the elevation profile.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the desired Coordinate Reference System (crs) for the profile.
double distanceScale() const
Returns the denominator of the distance scale.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setDistanceScale(double scale)
Sets the denominator of the distance scale.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
QList< QgsAbstractProfileSource * > sources() const
Returns the list of sources participating in the elevation profile.
void setRangeMethod(Qgis::ElevationProfileRangeMethod method)
Sets the method used to determine the elevation/distance ranges on the profile.
void setDistanceUnit(Qgis::DistanceUnit unit)
Sets the unit for the distance axis.
void setTolerance(double tolerance)
Sets the tolerance of the request (in crs() units).
QgsCoordinateReferenceSystem crs() const
Returns the desired Coordinate Reference System for the profile.
void setAtlasDriven(bool enabled)
Sets whether the profile curve will follow the current atlas feature.
double tolerance() const
Returns the tolerance of the request (in crs() units).
QgsLineSymbol * subsectionsSymbol()
Returns the symbol used to draw the subsections.
Qgis::DistanceUnit distanceUnit() const
Returns the units for the distance axis.
bool requiresRasterization() const override
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
void setSources(const QList< QgsAbstractProfileSource * > &sources)
Sets the list of sources participating in the elevation profile.
bool containsAdvancedEffects() const override
Returns true if the item contains contents with blend modes or transparency effects which can only be...
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
double elevationScale() const
Returns the denominator of the elevation scale.
QIcon icon() const override
Returns the item's icon.
void previewRefreshed()
Emitted whenever the item's preview has been refreshed.
void setProfileCurve(QgsCurve *curve)
Sets the cross section profile curve, which represents the line along which the profile should be gen...
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QgsProfileRequest profileRequest() const
Returns the profile request used to generate the elevation profile.
@ LayoutElevationProfile
Elevation profile item.
Contains settings and helpers relating to a render of a QgsLayoutItem.
virtual void drawFrame(QgsRenderContext &context)
Draws the frame around the item.
friend class QgsLayout
QColor backgroundColor(bool useDataDefined=true) const
Returns the background color for this item.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
void refreshItemSize()
Refreshes an item's size by rechecking it against any possible item fixed or minimum sizes.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void drawBackground(QgsRenderContext &context)
Draws the background for the item.
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
@ FlagOverridesPaint
Item overrides the default layout item painting method.
@ FlagDisableSceneCaching
Item should not have QGraphicsItem caching enabled.
void sizePositionChanged()
Emitted when the item's size or position changes.
bool frameEnabled() const
Returns true if the item includes a frame.
bool hasBackground() const
Returns true if the item has a background.
QFlags< Flag > Flags
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
friend class QgsLayoutItemElevationProfile
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
QPainter::CompositionMode blendMode() const
Returns the item's composition blending mode.
void backgroundTaskCountChanged(int count)
Emitted whenever the number of background tasks an item is executing changes.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ ElevationProfileMaximumDistance
Maximum distance value for elevation profile.
@ ElevationProfileElevationMinorInterval
Minor grid line interval for elevation profile elevation axis.
@ ElevationProfileDistanceMinorInterval
Minor grid line interval for elevation profile distance axis.
@ ElevationProfileMinimumDistance
Minimum distance value for elevation profile.
@ ElevationProfileMaximumElevation
Maximum elevation value for elevation profile.
@ ElevationProfileDistanceLabelInterval
Label interval for elevation profile distance axis.
@ ElevationProfileTolerance
Tolerance distance for elevation profiles.
@ ElevationProfileMinimumElevation
Minimum elevation value for elevation profile.
@ ElevationProfileElevationLabelInterval
Label interval for elevation profile elevation axis.
@ ElevationProfileDistanceMajorInterval
Major grid line interval for elevation profile distance axis.
@ ElevationProfileElevationMajorInterval
Major grid line interval for elevation profile elevation axis.
@ AllProperties
All properties for item.
double height() const
Returns the height of the size.
double width() const
Returns the width of the size.
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static Q_DECL_DEPRECATED double scaleFactorFromItemStyle(const QStyleOptionGraphicsItem *style)
Extracts the scale factor from an item style.
void refreshed()
Emitted when the layout has been refreshed and items should also be refreshed and updated.
A line symbol type, for rendering LineString and MultiLineString geometries.
Base class for all map layer types.
Definition qgsmaplayer.h:83
Perform transforms between map coordinates and device coordinates.
Defines the four margins of a rectangle.
Definition qgsmargins.h:40
void setBottom(double bottom)
Sets the bottom margin to bottom.
Definition qgsmargins.h:112
void setLeft(double left)
Sets the left margin to left.
Definition qgsmargins.h:94
void setRight(double right)
Sets the right margin to right.
Definition qgsmargins.h:106
void setTop(double top)
Sets the top margin to top.
Definition qgsmargins.h:100
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Encapsulates one or more plot series.
Definition qgsplot.h:303
Contains information about the context of a plot rendering operation.
Definition qgsplot.h:191
Generates and renders elevation profile plots.
void setSubsectionsSymbol(QgsLineSymbol *symbol)
Sets the symbol used to draw the subsections.
void cancelGenerationWithoutBlocking()
Triggers cancellation of the generation job without blocking.
void generateSynchronously()
Generate the profile results synchronously in this thread.
void generationFinished()
Emitted when the profile generation is finished (or canceled).
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
QgsProfileRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate expressions.
QgsProfileRequest & setTransformContext(const QgsCoordinateTransformContext &context)
Sets the transform context, for use when transforming coordinates from a source to the request's crs(...
QgsProfileRequest & setTerrainProvider(QgsAbstractTerrainProvider *provider)
Sets the terrain provider.
QgsProfileRequest & setTolerance(double tolerance)
Sets the tolerance of the request (in crs() units).
QgsProfileRequest & setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the desired Coordinate Reference System (crs) for the profile.
void profileSourceUnregistered(const QString &sourceId)
Signal emitted once a profile source is unregistered.
void profileSourceRegistered(const QString &sourceId, const QString &sourceName)
Signal emitted once a profile source is registered.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
A container for the context for various read/write operations on 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.
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
Scoped object for saving and restoring a QPainter object's state.
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE QString toAbbreviatedString(Qgis::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
Represents a vector layer which manages a vector based dataset.
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:8018
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7632
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7999
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7725
T qgsgeometry_cast(QgsAbstractGeometry *geom)
_LayerRef< QgsMapLayer > QgsMapLayerRef
TYPE * resolveWeakly(const QgsProject *project, MatchType matchType=MatchType::All)
Resolves the map layer by attempting to find a matching layer in a project using a weak match.
TYPE * get() const
Returns a pointer to the layer, or nullptr if the reference has not yet been matched to a layer.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the layer's properties from an XML element.