QGIS API Documentation 4.3.0-Master (35968c35f7e)
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 "qgspainting.h"
32#include "qgsplot.h"
33#include "qgsprofilerenderer.h"
34#include "qgsprofilerequest.h"
37#include "qgssymbollayerutils.h"
38#include "qgsterrainprovider.h"
39#include "qgsvectorlayer.h"
40
41#include <QString>
42#include <QTimer>
43
44#include "moc_qgslayoutitemelevationprofile.cpp"
45
46using namespace Qt::StringLiterals;
47
48#define CACHE_SIZE_LIMIT 5000
49
51class QgsLayoutItemElevationProfilePlot : public Qgs2DXyPlot
52{
53 public:
54 QgsLayoutItemElevationProfilePlot() {}
55
56 void setRenderer( QgsProfilePlotRenderer *renderer )
57 {
58 // cppcheck-suppress danglingLifetime
59 mRenderer = renderer;
60 }
61
62 void renderContent( QgsRenderContext &rc, QgsPlotRenderContext &, const QRectF &plotArea, const QgsPlotData & ) override
63 {
64 if ( mRenderer )
65 {
66 const double distanceMin = xMinimum() * xScale;
67 const double distanceMax = xMaximum() * xScale;
68 rc.painter()->translate( plotArea.left(), plotArea.top() );
69 mRenderer->render( rc, plotArea.width(), plotArea.height(), distanceMin, distanceMax, yMinimum(), yMaximum() );
70 rc.painter()->translate( -plotArea.left(), -plotArea.top() );
71 mRenderer->renderSubsectionsIndicator( rc, plotArea, distanceMin, distanceMax, yMinimum(), yMaximum() );
72 }
73 }
74
75 double xScale = 1;
76
77 private:
78 QgsProfilePlotRenderer *mRenderer = nullptr;
79};
81
84 , mPlot( std::make_unique< QgsLayoutItemElevationProfilePlot >() )
85{
86 mBackgroundUpdateTimer = new QTimer( this );
87 mBackgroundUpdateTimer->setSingleShot( true );
88 connect( mBackgroundUpdateTimer, &QTimer::timeout, this, &QgsLayoutItemElevationProfile::recreateCachedImageInBackground );
89
90 setCacheMode( QGraphicsItem::NoCache );
91
92 if ( mLayout )
93 {
95 }
96
97 connect( this, &QgsLayoutItem::sizePositionChanged, this, [this] { invalidateCache(); } );
98
99 //default to no background
100 setBackgroundEnabled( false );
101
102 connect( QgsApplication::profileSourceRegistry(), &QgsProfileSourceRegistry::profileSourceRegistered, this, &QgsLayoutItemElevationProfile::setSourcesPrivate );
103 connect( QgsApplication::profileSourceRegistry(), &QgsProfileSourceRegistry::profileSourceUnregistered, this, &QgsLayoutItemElevationProfile::setSourcesPrivate );
104
105 setSourcesPrivate(); // Initialize with registered sources
106}
107
109{
110 if ( mRenderJob )
111 {
112 disconnect( mRenderJob.get(), &QgsProfilePlotRenderer::generationFinished, this, &QgsLayoutItemElevationProfile::profileGenerationFinished );
114 mRenderJob->cancelGeneration(); // blocks
115 mPainter->end();
116 }
117}
118
123
128
130{
131 return QgsApplication::getThemeIcon( u"mLayoutItemElevationProfile.svg"_s );
132}
133
135{
137
138 bool forceUpdate = false;
139
142 {
143 double value = mTolerance;
144
145 bool ok = false;
147
148 if ( !ok )
149 {
150 QgsMessageLog::logMessage( tr( "Elevation profile tolerance expression eval error" ) );
151 }
152 else
153 {
154 mTolerance = value;
155 }
156
157 forceUpdate = true;
158 }
159
162 {
163 double value = mPlot->xMinimum();
164
165 bool ok = false;
167
168 if ( !ok )
169 {
170 QgsMessageLog::logMessage( tr( "Elevation profile minimum distance expression eval error" ) );
171 }
172 else
173 {
174 mPlot->setXMinimum( value );
175 }
176
177 forceUpdate = true;
178 }
179
182 {
183 double value = mPlot->xMaximum();
184
185 bool ok = false;
187
188 if ( !ok )
189 {
190 QgsMessageLog::logMessage( tr( "Elevation profile maximum distance expression eval error" ) );
191 }
192 else
193 {
194 mPlot->setXMaximum( value );
195 }
196
197 forceUpdate = true;
198 }
199
202 {
203 double value = mPlot->yMinimum();
204
205 bool ok = false;
207
208 if ( !ok )
209 {
210 QgsMessageLog::logMessage( tr( "Elevation profile minimum elevation expression eval error" ) );
211 }
212 else
213 {
214 mPlot->setYMinimum( value );
215 }
216
217 forceUpdate = true;
218 }
219
222 {
223 double value = mPlot->yMaximum();
224
225 bool ok = false;
227
228 if ( !ok )
229 {
230 QgsMessageLog::logMessage( tr( "Elevation profile maximum elevation expression eval error" ) );
231 }
232 else
233 {
234 mPlot->setYMaximum( value );
235 }
236
237 forceUpdate = true;
238 }
239
242 {
243 double value = mPlot->xAxis().gridIntervalMajor();
244
245 bool ok = false;
247
248 if ( !ok )
249 {
250 QgsMessageLog::logMessage( tr( "Elevation profile distance axis major interval expression eval error" ) );
251 }
252 else
253 {
254 mPlot->xAxis().setGridIntervalMajor( value );
255 }
256
257 forceUpdate = true;
258 }
259
262 {
263 double value = mPlot->xAxis().gridIntervalMinor();
264
265 bool ok = false;
267
268 if ( !ok )
269 {
270 QgsMessageLog::logMessage( tr( "Elevation profile distance axis minor interval expression eval error" ) );
271 }
272 else
273 {
274 mPlot->xAxis().setGridIntervalMinor( value );
275 }
276
277 forceUpdate = true;
278 }
279
282 {
283 double value = mPlot->xAxis().labelInterval();
284
285 bool ok = false;
287
288 if ( !ok )
289 {
290 QgsMessageLog::logMessage( tr( "Elevation profile distance axis label interval expression eval error" ) );
291 }
292 else
293 {
294 mPlot->xAxis().setLabelInterval( value );
295 }
296
297 forceUpdate = true;
298 }
299
302 {
303 double value = mPlot->yAxis().gridIntervalMajor();
304
305 bool ok = false;
307
308 if ( !ok )
309 {
310 QgsMessageLog::logMessage( tr( "Elevation profile elevation axis major interval expression eval error" ) );
311 }
312 else
313 {
314 mPlot->yAxis().setGridIntervalMajor( value );
315 }
316
317 forceUpdate = true;
318 }
319
322 {
323 double value = mPlot->yAxis().gridIntervalMinor();
324
325 bool ok = false;
327
328 if ( !ok )
329 {
330 QgsMessageLog::logMessage( tr( "Elevation profile elevation axis minor interval expression eval error" ) );
331 }
332 else
333 {
334 mPlot->yAxis().setGridIntervalMinor( value );
335 }
336
337 forceUpdate = true;
338 }
339
342 {
343 double value = mPlot->yAxis().labelInterval();
344
345 bool ok = false;
347
348 if ( !ok )
349 {
350 QgsMessageLog::logMessage( tr( "Elevation profile elevation axis label interval expression eval error" ) );
351 }
352 else
353 {
354 mPlot->yAxis().setLabelInterval( value );
355 }
356
357 forceUpdate = true;
358 }
359
362 {
363 double value = mPlot->margins().left();
364
365 bool ok = false;
366 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginLeft, context, value, &ok );
367
368 if ( !ok )
369 {
370 QgsMessageLog::logMessage( tr( "Elevation profile left margin expression eval error" ) );
371 }
372 else
373 {
374 QgsMargins margins = mPlot->margins();
375 margins.setLeft( value );
376 mPlot->setMargins( margins );
377 }
378
379 forceUpdate = true;
380 }
381
384 {
385 double value = mPlot->margins().right();
386
387 bool ok = false;
388 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginRight, context, value, &ok );
389
390 if ( !ok )
391 {
392 QgsMessageLog::logMessage( tr( "Elevation profile right margin expression eval error" ) );
393 }
394 else
395 {
396 QgsMargins margins = mPlot->margins();
397 margins.setRight( value );
398 mPlot->setMargins( margins );
399 }
400
401 forceUpdate = true;
402 }
403
406 {
407 double value = mPlot->margins().top();
408
409 bool ok = false;
410 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginTop, context, value, &ok );
411
412 if ( !ok )
413 {
414 QgsMessageLog::logMessage( tr( "Elevation profile top margin expression eval error" ) );
415 }
416 else
417 {
418 QgsMargins margins = mPlot->margins();
419 margins.setTop( value );
420 mPlot->setMargins( margins );
421 }
422
423 forceUpdate = true;
424 }
425
428 {
429 double value = mPlot->margins().bottom();
430
431 bool ok = false;
432 value = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MarginBottom, context, value, &ok );
433
434 if ( !ok )
435 {
436 QgsMessageLog::logMessage( tr( "Elevation profile bottom margin expression eval error" ) );
437 }
438 else
439 {
440 QgsMargins margins = mPlot->margins();
441 margins.setBottom( value );
442 mPlot->setMargins( margins );
443 }
444
445 forceUpdate = true;
446 }
447
448 if ( forceUpdate )
449 {
450 mCacheInvalidated = true;
451
453 update();
454 }
455
457}
458
463
465{
466 return blendMode() != QPainter::CompositionMode_SourceOver;
467}
468
470{
471 return mEvaluatedOpacity < 1.0;
472}
473
475{
476 return mPlot.get();
477}
478
480{
481 return mPlot.get();
482}
483
484QList<QgsMapLayer *> QgsLayoutItemElevationProfile::layers() const
485{
486 return _qgis_listRefToRaw( mLayers );
487}
488
489void QgsLayoutItemElevationProfile::setLayers( const QList<QgsMapLayer *> &layers )
490{
491 if ( layers == _qgis_listRefToRaw( mLayers ) )
492 return;
493
494 mLayers = _qgis_listRawToRef( layers );
496}
497
498QList<QgsAbstractProfileSource *> QgsLayoutItemElevationProfile::sources() const
499{
500 if ( mSources.isEmpty() && !mLayers.isEmpty() )
501 {
502 // Legacy: If we have layers, extract their sources and return them.
503 // We don't set mSources here, because we want the previous check to
504 // continue failing if only layers are set.
505 // TODO: Remove this if block in QGIS 5.0 along with setLayers/layers methods.
506 QList< QgsAbstractProfileSource * > sources;
507 const QList<QgsMapLayer *> layersToGenerate = layers();
508 sources.reserve( layersToGenerate.size() );
509
510 for ( QgsMapLayer *layer : layersToGenerate )
511 {
512 if ( QgsAbstractProfileSource *source = layer->profileSource() )
513 sources << source;
514 }
515
516 // More legacy: elevation profile layers are in opposite direction
517 // to what the layer tree requires, and are in the same direction as
518 // the renderer expects, but since we reverse sources() (i.e., layers)
519 // before passing them to the renderer, then we need to reverse here first.
520 std::reverse( sources.begin(), sources.end() );
521
522 return sources;
523 }
524
525 QList< QgsAbstractProfileSource * > sources;
526 sources.reserve( mSources.count() );
527 for ( const auto &source : mSources )
528 {
529 if ( const QgsMapLayerRef *layerRef = std::get_if< QgsMapLayerRef >( &source ) )
530 {
531 if ( QgsMapLayer *layer = layerRef->get() )
532 {
533 sources << layer->profileSource();
534 }
535 }
536 else if ( auto profileSource = std::get_if< QgsAbstractProfileSource * >( &source ) )
537 {
538 if ( QgsApplication::profileSourceRegistry()->findSourceById( ( *profileSource )->profileSourceId() ) )
539 {
540 sources << *profileSource;
541 }
542 }
543 }
544
545 return sources;
546}
547
548void QgsLayoutItemElevationProfile::setSources( const QList<QgsAbstractProfileSource *> &sources )
549{
550 mSources.clear();
551 mSources.reserve( sources.count() );
552 for ( QgsAbstractProfileSource *profileSource : sources )
553 {
554 if ( auto layer = dynamic_cast<QgsMapLayer *>( profileSource ) )
555 {
556 mSources << QgsMapLayerRef( layer );
557 }
558 else if ( QgsApplication::profileSourceRegistry()->findSourceById( profileSource->profileSourceId() ) )
559 {
560 mSources << profileSource;
561 }
562 }
563
565}
566
568{
569 mCurve.reset( curve );
571}
572
574{
575 return mCurve.get();
576}
577
579{
580 if ( mCrs == crs )
581 return;
582
583 mCrs = crs;
585}
586
591
593{
594 if ( mTolerance == tolerance )
595 return;
596
597 mTolerance = tolerance;
599}
600
602{
603 return mTolerance;
604}
605
607{
608 mAtlasDriven = enabled;
609}
610
612{
613 QgsProfileRequest req( mCurve ? mCurve.get()->clone() : nullptr );
614
615 req.setCrs( mCrs );
616 req.setTolerance( mTolerance );
618 if ( mLayout )
619 {
620 if ( QgsProject *project = mLayout->project() )
621 {
622 req.setTransformContext( project->transformContext() );
623 if ( QgsAbstractTerrainProvider *provider = project->elevationProperties()->terrainProvider() )
624 {
625 req.setTerrainProvider( provider->clone() );
626 }
627 }
628 }
629 return req;
630}
631
632void QgsLayoutItemElevationProfile::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget * )
633{
634 if ( !mLayout || !painter || !painter->device() || !mUpdatesEnabled )
635 {
636 return;
637 }
638 if ( !shouldDrawItem() )
639 {
640 return;
641 }
642
643 QRectF thisPaintRect = rect();
644 if ( qgsDoubleNear( thisPaintRect.width(), 0.0 ) || qgsDoubleNear( thisPaintRect.height(), 0 ) )
645 return;
646
648 calculateAxisRanges( rc );
649
650 if ( mLayout->renderContext().isPreviewRender() )
651 {
653
654 QgsScopedQPainterState painterState( painter );
655 painter->setClipRect( thisPaintRect );
656 if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
657 {
658 // No initial render available - so draw some preview text alerting user
659 painter->setBrush( QBrush( QColor( 125, 125, 125, 125 ) ) );
660 painter->drawRect( thisPaintRect );
661 painter->setBrush( Qt::NoBrush );
662 QFont messageFont;
663 messageFont.setPointSize( 12 );
664 painter->setFont( messageFont );
665 painter->setPen( QColor( 255, 255, 255, 255 ) );
666 painter->drawText( thisPaintRect, Qt::AlignCenter | Qt::AlignHCenter, tr( "Rendering profile" ) );
667
668 if (
669 ( mRenderJob && mCacheInvalidated && !mDrawingPreview ) // current job was invalidated - start a new one
670 || ( !mRenderJob && !mDrawingPreview ) // this is the profiles's very first paint - trigger a cache update
671 )
672 {
673 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
674 mBackgroundUpdateTimer->start( 1 );
675 }
676 }
677 else
678 {
679 if ( mCacheInvalidated && !mDrawingPreview )
680 {
681 // cache was invalidated - trigger a background update
682 mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
683 mBackgroundUpdateTimer->start( 1 );
684 }
685
686 //Background color is already included in cached image, so no need to draw
687
688 double imagePixelWidth = mCacheFinalImage->width(); //how many pixels of the image are for the map extent?
689 double scale = rect().width() / imagePixelWidth;
690
691 QgsScopedQPainterState rotatedPainterState( painter );
692
693 painter->scale( scale, scale );
694 painter->setCompositionMode( blendModeForRender() );
695 painter->drawImage( 0, 0, *mCacheFinalImage );
696 }
697
698 painter->setClipRect( thisPaintRect, Qt::NoClip );
699
700 if ( frameEnabled() )
701 {
703 }
704 }
705 else
706 {
707 if ( mDrawing )
708 return;
709
710 mDrawing = true;
711 QPaintDevice *paintDevice = painter->device();
712 if ( !paintDevice )
713 return;
714
715 QSizeF layoutSize = mLayout->convertToLayoutUnits( sizeWithUnits() );
716
717 if ( mLayout->renderContext().flags() & Qgis::LayoutRenderFlag::LosslessImageRendering )
718 painter->setRenderHint( QPainter::LosslessImageRendering, true );
719
720 mPlot->xScale = QgsUnitTypes::fromUnitToUnitFactor( mDistanceUnit, mCrs.mapUnits() );
721
722 if ( !qgsDoubleNear( layoutSize.width(), 0.0 ) && !qgsDoubleNear( layoutSize.height(), 0.0 ) )
723 {
724 const bool forceVector = mLayout && mLayout->renderContext().rasterizedRenderingPolicy() == Qgis::RasterizedRenderingPolicy::ForceVector;
725 if ( ( containsAdvancedEffects() || ( blendModeForRender() != QPainter::CompositionMode_SourceOver ) ) && !forceVector )
726 {
727 // rasterize
728 double destinationDpi = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter ) * 25.4;
729 double layoutUnitsInInches = mLayout ? mLayout->convertFromLayoutUnits( 1, Qgis::LayoutUnit::Inches ).length() : 1;
730 int widthInPixels = static_cast< int >( std::round( boundingRect().width() * layoutUnitsInInches * destinationDpi ) );
731 int heightInPixels = static_cast< int >( std::round( boundingRect().height() * layoutUnitsInInches * destinationDpi ) );
732 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
733
734 image.fill( Qt::transparent );
735 image.setDotsPerMeterX( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
736 image.setDotsPerMeterY( static_cast< int >( std::round( 1000 * destinationDpi / 25.4 ) ) );
737 double dotsPerMM = destinationDpi / 25.4;
738 layoutSize *= dotsPerMM; // output size will be in dots (pixels)
739 QPainter p( &image );
740 preparePainter( &p );
741
744
745 p.scale( dotsPerMM, dotsPerMM );
746 if ( hasBackground() )
747 {
749 }
750
751 p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
752
753 const double mapUnitsPerPixel = static_cast<double>( mPlot->xMaximum() - mPlot->xMinimum() ) * mPlot->xScale / layoutSize.width();
754 rc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
755
756 QList< QgsAbstractProfileSource *> sourcesToRender = sources();
757 std::reverse( sourcesToRender.begin(), sourcesToRender.end() ); // sources are rendered from bottom to top
758
759 QgsProfilePlotRenderer renderer( sourcesToRender, profileRequest() );
760 std::unique_ptr<QgsLineSymbol> rendererSubSectionsSymbol( subsectionsSymbol() ? subsectionsSymbol()->clone() : nullptr );
761 renderer.setSubsectionsSymbol( rendererSubSectionsSymbol.release() );
762
763 renderer.generateSynchronously();
764 mPlot->setRenderer( &renderer );
765
766 // size must be in pixels, not layout units
767 mPlot->setSize( layoutSize );
768
769 QgsPlotRenderContext plotContext;
770 mPlot->render( rc, plotContext );
771
772 mPlot->setRenderer( nullptr );
773
774 p.scale( dotsPerMM, dotsPerMM );
775
776 if ( frameEnabled() )
777 {
779 }
780
781 QgsScopedQPainterState painterState( painter );
782 painter->setCompositionMode( blendModeForRender() );
783 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
784 painter->drawImage( 0, 0, image );
785 painter->scale( dotsPerMM, dotsPerMM );
786 }
787 else
788 {
791
792 // Fill with background color
793 if ( hasBackground() )
794 {
796 }
797
798 QgsScopedQPainterState painterState( painter );
799 QgsScopedQPainterState stagedPainterState( painter );
800 double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
801 layoutSize *= dotsPerMM; // output size will be in dots (pixels)
802 painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
803
804 const double mapUnitsPerPixel = static_cast<double>( mPlot->xMaximum() - mPlot->xMinimum() ) * mPlot->xScale / layoutSize.width();
805 rc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
806
807 QList< QgsAbstractProfileSource *> sourcesToRender = sources();
808 std::reverse( sourcesToRender.begin(), sourcesToRender.end() ); // sources are rendered from bottom to top
809
810 QgsProfilePlotRenderer renderer( sourcesToRender, profileRequest() );
811 std::unique_ptr<QgsLineSymbol> rendererSubSectionsSymbol( subsectionsSymbol() ? subsectionsSymbol()->clone() : nullptr );
812 renderer.setSubsectionsSymbol( rendererSubSectionsSymbol.release() );
813
814 // TODO
815 // we should be able to call renderer.start()/renderer.waitForFinished() here and
816 // benefit from parallel source generation. BUT
817 // for some reason the QtConcurrent::map call in start() never triggers
818 // the actual background thread execution.
819 // So for now just generate the results one by one
820 renderer.generateSynchronously();
821 mPlot->setRenderer( &renderer );
822
823 // size must be in pixels, not layout units
824 mPlot->setSize( layoutSize );
825
826 QgsPlotRenderContext plotContext;
827 mPlot->render( rc, plotContext );
828
829 mPlot->setRenderer( nullptr );
830
831 painter->setClipRect( thisPaintRect, Qt::NoClip );
832
833 if ( frameEnabled() )
834 {
836 }
837 }
838 }
839
840 mDrawing = false;
841 }
842}
843
845{
846 if ( mAtlasDriven && mLayout && mLayout->reportContext().layer() )
847 {
848 if ( QgsVectorLayer *layer = mLayout->reportContext().layer() )
849 {
850 mCrs = layer->crs();
851 }
852 const QgsGeometry curveGeom( mLayout->reportContext().currentGeometry( mCrs ) );
853 if ( const QgsAbstractGeometry *geom = curveGeom.constGet() )
854 {
855 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom->simplifiedTypeRef() ) )
856 {
857 mCurve.reset( curve->clone() );
858 }
859 }
860 }
861
864}
865
867{
868 if ( mDrawing )
869 return;
870
871 mCacheInvalidated = true;
872 update();
873}
874
877
878bool QgsLayoutItemElevationProfile::writePropertiesToElement( QDomElement &layoutProfileElem, QDomDocument &doc, const QgsReadWriteContext &rwContext ) const
879{
880 {
881 QDomElement plotElement = doc.createElement( u"plot"_s );
882 mPlot->writeXml( plotElement, doc, rwContext );
883 layoutProfileElem.appendChild( plotElement );
884 }
885
887 {
888 layoutProfileElem.setAttribute( u"rangeMethod"_s, qgsEnumValueToKey( mRangeMethod ) );
889 }
890 if ( !qgsDoubleNear( mDistanceScale, 1000 ) )
891 {
892 layoutProfileElem.setAttribute( u"distanceScale"_s, qgsDoubleToString( mDistanceScale ) );
893 }
894 if ( !qgsDoubleNear( mElevationScale, 10 ) )
895 {
896 layoutProfileElem.setAttribute( u"elevationScale"_s, qgsDoubleToString( mElevationScale ) );
897 }
898
899 layoutProfileElem.setAttribute( u"distanceUnit"_s, qgsEnumValueToKey( mDistanceUnit ) );
900
901 layoutProfileElem.setAttribute( u"tolerance"_s, mTolerance );
902 layoutProfileElem.setAttribute( u"atlasDriven"_s, mAtlasDriven ? u"1"_s : u"0"_s );
903 if ( mCrs.isValid() )
904 {
905 QDomElement crsElem = doc.createElement( u"crs"_s );
906 mCrs.writeXml( crsElem, doc );
907 layoutProfileElem.appendChild( crsElem );
908 }
909 if ( mCurve )
910 {
911 QDomElement curveElem = doc.createElement( u"curve"_s );
912 curveElem.appendChild( doc.createTextNode( mCurve->asWkt() ) );
913 layoutProfileElem.appendChild( curveElem );
914 }
915
916 {
917 QDomElement layersElement = doc.createElement( u"layers"_s );
918 for ( const QgsMapLayerRef &layer : mLayers )
919 {
920 QDomElement layerElement = doc.createElement( u"layer"_s );
921 layer.writeXml( layerElement, rwContext );
922 layersElement.appendChild( layerElement );
923 }
924 layoutProfileElem.appendChild( layersElement );
925 }
926
927 {
928 QDomElement sourcesElement = doc.createElement( u"profileSources"_s );
929 for ( const auto &source : mSources )
930 {
931 if ( const QgsMapLayerRef *layerRef = std::get_if< QgsMapLayerRef >( &source ) )
932 {
933 if ( layerRef->get() )
934 {
935 QDomElement sourceElement = doc.createElement( u"profileLayerSource"_s );
936 layerRef->writeXml( sourceElement, rwContext );
937 sourcesElement.appendChild( sourceElement );
938 }
939 }
940 else if ( auto profileSource = std::get_if< QgsAbstractProfileSource * >( &source ) )
941 {
942 if ( QgsApplication::profileSourceRegistry()->findSourceById( ( *profileSource )->profileSourceId() ) )
943 {
944 QDomElement sourceElement = doc.createElement( u"profileCustomSource"_s );
945 sourceElement.setAttribute( u"id"_s, ( *profileSource )->profileSourceId() );
946 sourcesElement.appendChild( sourceElement );
947 }
948 }
949 }
950
951 layoutProfileElem.appendChild( sourcesElement );
952 }
953
954 if ( mSubsectionsSymbol )
955 {
956 QDomElement subsectionsElement = doc.createElement( u"subsections"_s );
957 const QDomElement symbolElement = QgsSymbolLayerUtils::saveSymbol( u"subsections"_s, mSubsectionsSymbol.get(), doc, rwContext );
958 subsectionsElement.appendChild( symbolElement );
959 layoutProfileElem.appendChild( subsectionsElement );
960 }
961
962 return true;
963}
964
965bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext &context )
966{
967 const QDomElement plotElement = itemElem.firstChildElement( u"plot"_s );
968 if ( !plotElement.isNull() )
969 {
970 mPlot->readXml( plotElement, context );
971 }
972
973 const QDomNodeList crsNodeList = itemElem.elementsByTagName( u"crs"_s );
975 if ( !crsNodeList.isEmpty() )
976 {
977 const QDomElement crsElem = crsNodeList.at( 0 ).toElement();
978 crs.readXml( crsElem );
979 }
980 mCrs = crs;
981
982 mRangeMethod = qgsEnumKeyToValue( itemElem.attribute( u"rangeMethod"_s ), Qgis::ElevationProfileRangeMethod::ManualRange );
983 mDistanceScale = itemElem.attribute( u"distanceScale"_s ).toDouble();
984 mElevationScale = itemElem.attribute( u"elevationScale"_s ).toDouble();
985
986 setDistanceUnit( qgsEnumKeyToValue( itemElem.attribute( u"distanceUnit"_s ), mCrs.mapUnits() ) );
987
988 const QDomNodeList curveNodeList = itemElem.elementsByTagName( u"curve"_s );
989 if ( !curveNodeList.isEmpty() )
990 {
991 const QDomElement curveElem = curveNodeList.at( 0 ).toElement();
992 const QgsGeometry curve = QgsGeometry::fromWkt( curveElem.text() );
993 if ( const QgsCurve *curveGeom = qgsgeometry_cast< const QgsCurve * >( curve.constGet() ) )
994 {
995 mCurve.reset( curveGeom->clone() );
996 }
997 else
998 {
999 mCurve.reset();
1000 }
1001 }
1002
1003 mTolerance = itemElem.attribute( u"tolerance"_s ).toDouble();
1004 mAtlasDriven = static_cast< bool >( itemElem.attribute( u"atlasDriven"_s, u"0"_s ).toInt() );
1005
1006 {
1007 mLayers.clear();
1008 const QDomElement layersElement = itemElem.firstChildElement( u"layers"_s );
1009 QDomElement layerElement = layersElement.firstChildElement( u"layer"_s );
1010 while ( !layerElement.isNull() )
1011 {
1012 QgsMapLayerRef ref;
1013 ref.readXml( layerElement, context );
1014 ref.resolveWeakly( mLayout->project() );
1015 mLayers.append( ref );
1016
1017 layerElement = layerElement.nextSiblingElement( u"layer"_s );
1018 }
1019 }
1020
1021 {
1022 mSources.clear();
1023 mSources.reserve( itemElem.elementsByTagName( u"profileSources"_s ).count() );
1024 const QDomElement sourcesElement = itemElem.firstChildElement( u"profileSources"_s );
1025 QDomElement sourceElement = sourcesElement.firstChildElement();
1026 while ( !sourceElement.isNull() )
1027 {
1028 if ( sourceElement.tagName() == "profileCustomSource"_L1 )
1029 {
1030 const QString sourceId = sourceElement.attribute( u"id"_s );
1031 if ( QgsAbstractProfileSource *profileSource = QgsApplication::profileSourceRegistry()->findSourceById( sourceId ) )
1032 {
1033 mSources << profileSource;
1034 }
1035 }
1036 else if ( sourceElement.tagName() == "profileLayerSource"_L1 )
1037 {
1038 QgsMapLayerRef ref;
1039 ref.readXml( sourceElement, context );
1040 ref.resolveWeakly( mLayout->project() );
1041 if ( ref.get() )
1042 {
1043 mSources << ref;
1044 }
1045 }
1046
1047 sourceElement = sourceElement.nextSiblingElement();
1048 }
1049 }
1050
1051 const QDomElement subsectionsElement = itemElem.firstChildElement( u"subsections"_s );
1052 const QDomElement symbolsElement = subsectionsElement.firstChildElement( u"symbol"_s );
1053 if ( !symbolsElement.isNull() )
1054 {
1055 std::unique_ptr< QgsLineSymbol > subSectionsSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol >( symbolsElement, context );
1056 if ( subSectionsSymbol )
1057 {
1058 setSubsectionsSymbol( subSectionsSymbol.release() );
1059 }
1060 }
1061
1062 return true;
1063}
1064
1065void QgsLayoutItemElevationProfile::recreateCachedImageInBackground()
1066{
1067 if ( mRenderJob )
1068 {
1069 disconnect( mRenderJob.get(), &QgsProfilePlotRenderer::generationFinished, this, &QgsLayoutItemElevationProfile::profileGenerationFinished );
1070 QgsProfilePlotRenderer *oldJob = mRenderJob.release();
1071 QPainter *oldPainter = mPainter.release();
1072 QImage *oldImage = mCacheRenderingImage.release();
1073 connect( oldJob, &QgsProfilePlotRenderer::generationFinished, this, [oldPainter, oldJob, oldImage] {
1074 oldJob->deleteLater();
1075 delete oldPainter;
1076 delete oldImage;
1077 } );
1079 }
1080 else
1081 {
1082 mCacheRenderingImage.reset( nullptr );
1084 }
1085
1086 Q_ASSERT( !mRenderJob );
1087 Q_ASSERT( !mPainter );
1088 Q_ASSERT( !mCacheRenderingImage );
1089
1090 const QSizeF layoutSize = mLayout->convertToLayoutUnits( sizeWithUnits() );
1091 double widthLayoutUnits = layoutSize.width();
1092 double heightLayoutUnits = layoutSize.height();
1093
1094 int w = static_cast< int >( std::round( widthLayoutUnits * mPreviewScaleFactor ) );
1095 int h = static_cast< int >( std::round( heightLayoutUnits * mPreviewScaleFactor ) );
1096
1097 // limit size of image for better performance
1098 if ( w > 5000 || h > 5000 )
1099 {
1100 if ( w > h )
1101 {
1102 w = 5000;
1103 h = static_cast< int>( std::round( w * heightLayoutUnits / widthLayoutUnits ) );
1104 }
1105 else
1106 {
1107 h = 5000;
1108 w = static_cast< int >( std::round( h * widthLayoutUnits / heightLayoutUnits ) );
1109 }
1110 }
1111
1112 if ( w <= 0 || h <= 0 )
1113 return;
1114
1115 mCacheRenderingImage = std::make_unique<QImage>( w, h, QImage::Format_ARGB32 );
1116
1117 // set DPI of the image
1118 mCacheRenderingImage->setDotsPerMeterX( static_cast< int >( std::round( 1000 * w / widthLayoutUnits ) ) );
1119 mCacheRenderingImage->setDotsPerMeterY( static_cast< int >( std::round( 1000 * h / heightLayoutUnits ) ) );
1120
1121 //start with empty fill to avoid artifacts
1122 mCacheRenderingImage->fill( Qt::transparent );
1123 if ( hasBackground() )
1124 {
1125 //Initially fill image with specified background color
1126 mCacheRenderingImage->fill( backgroundColor().rgba() );
1127 }
1128
1129 mCacheInvalidated = false;
1130 mPainter = std::make_unique<QPainter>( mCacheRenderingImage.get() );
1131
1132 QList< QgsAbstractProfileSource *> sourcesToRender = sources();
1133 std::reverse( sourcesToRender.begin(), sourcesToRender.end() ); // sources are rendered from bottom to top
1134
1135 mRenderJob = std::make_unique< QgsProfilePlotRenderer >( sourcesToRender, profileRequest() );
1136 std::unique_ptr<QgsLineSymbol> rendererSubSectionsSymbol( subsectionsSymbol() ? subsectionsSymbol()->clone() : nullptr );
1137 mRenderJob->setSubsectionsSymbol( rendererSubSectionsSymbol.release() );
1138 connect( mRenderJob.get(), &QgsProfilePlotRenderer::generationFinished, this, &QgsLayoutItemElevationProfile::profileGenerationFinished );
1139 mRenderJob->startGeneration();
1140
1141 mDrawingPreview = false;
1142}
1143
1144void QgsLayoutItemElevationProfile::profileGenerationFinished()
1145{
1146 mPlot->setRenderer( mRenderJob.get() );
1147
1148 QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( mLayout, mPainter.get() );
1149
1150 mPlot->xScale = QgsUnitTypes::fromUnitToUnitFactor( mDistanceUnit, mCrs.mapUnits() );
1151
1152 const double mapUnitsPerPixel = static_cast< double >( mPlot->xMaximum() - mPlot->xMinimum() ) * mPlot->xScale / mCacheRenderingImage->size().width();
1153 rc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
1154
1155 // size must be in pixels, not layout units
1156 mPlot->setSize( mCacheRenderingImage->size() );
1157
1158 QgsPlotRenderContext plotContext;
1159 mPlot->render( rc, plotContext );
1160
1161 mPlot->setRenderer( nullptr );
1162
1163 mPainter->end();
1164 mRenderJob.reset( nullptr );
1165 mPainter.reset( nullptr );
1166 mCacheFinalImage = std::move( mCacheRenderingImage );
1168 update();
1169 emit previewRefreshed();
1170}
1171
1173{
1174 return mDistanceUnit;
1175}
1176
1178{
1179 mDistanceUnit = unit;
1180
1181 switch ( mDistanceUnit )
1182 {
1231 mPlot->xAxis().setLabelSuffix( u" %1"_s.arg( QgsUnitTypes::toAbbreviatedString( mDistanceUnit ) ) );
1232 break;
1233
1235 mPlot->xAxis().setLabelSuffix( QObject::tr( "°" ) );
1236 break;
1237
1239 mPlot->xAxis().setLabelSuffix( QString() );
1240 break;
1241 }
1242}
1243
1245{
1246 mSubsectionsSymbol.reset( symbol );
1247}
1248
1249void QgsLayoutItemElevationProfile::setSourcesPrivate()
1250{
1251 mSources.clear();
1252 mSources.reserve( QgsApplication::profileSourceRegistry()->profileSources().count() );
1253 for ( QgsAbstractProfileSource *source : QgsApplication::profileSourceRegistry()->profileSources() )
1254 mSources << source;
1255}
1256
1257void QgsLayoutItemElevationProfile::calculateAxisRanges( QgsRenderContext &context )
1258{
1259 if ( !mLayout )
1260 return;
1261
1262 switch ( mRangeMethod )
1263 {
1265 return; // nothing to do - axis range can't change!
1266
1268 break;
1269 }
1270
1271 const double mmToPainterUnits = context.convertToPainterUnits( 1.0, Qgis::RenderUnit::Millimeters );
1272 if ( qgsDoubleNear( mmToPainterUnits, 0.0 ) )
1273 return;
1274
1275 const QSizeF layoutSize = mLayout->convertToLayoutUnits( sizeWithUnits() );
1276 QgsLayoutSize layoutSizeMm = mLayout->convertFromLayoutUnits( layoutSize, Qgis::LayoutUnit::Millimeters );
1277
1278 mPlot->setSize( layoutSizeMm.toQSizeF() * mmToPainterUnits );
1279
1280 QgsPlotRenderContext plotContext;
1281
1282 // We need to do an iterative approach here. First we set a rough range based on the total size of the item.
1283 // But this is NOT correct, because that total size also includes the area outside of the plot itself (ie axis labels etc).
1284 // So then we set that range, and grab the interiorPlotArea corresponding to the plot with those initial guess set of axis labels.
1285 // From there we iterate, using the interiorPlotArea each time until we converge on the desired scale.
1286
1287 // first guess - assuming the whole item is the interior (incorrect!)
1288 if ( mDistanceScale > 0 )
1289 {
1290 const double widthMeters = layoutSizeMm.width() / 1000;
1291 const double distanceRange = QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, mDistanceUnit ) * widthMeters * mDistanceScale;
1292 mPlot->setXMinimum( 0 );
1293 mPlot->setXMaximum( distanceRange );
1294 }
1295 if ( mElevationScale > 0 )
1296 {
1297 const double heightMeters = layoutSizeMm.height() / 1000;
1298 const double elevationRange = heightMeters * mElevationScale;
1299 mPlot->setYMaximum( mPlot->yMinimum() + elevationRange );
1300 }
1301
1302 // now we start the iteration
1303 QRectF previousPlotArea;
1304 for ( int iteration = 0; iteration < 5; ++iteration )
1305 {
1306 const QRectF currentPlotArea = mPlot->interiorPlotArea( context, plotContext );
1307 if ( qgsDoubleNear( currentPlotArea.width(), previousPlotArea.width(), 1 ) && qgsDoubleNear( currentPlotArea.height(), previousPlotArea.height(), 1 ) )
1308 break;
1309
1310 previousPlotArea = currentPlotArea;
1311
1312 if ( mDistanceScale > 0 )
1313 {
1314 const double widthMm = currentPlotArea.width() / mmToPainterUnits;
1315 const double widthMeters = widthMm / 1000.0;
1316 const double distanceRange = QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, mDistanceUnit ) * widthMeters * mDistanceScale;
1317 mPlot->setXMaximum( distanceRange );
1318 }
1319
1320 if ( mElevationScale > 0 )
1321 {
1322 const double heightMm = currentPlotArea.height() / mmToPainterUnits;
1323 const double heightMeters = heightMm / 1000.0;
1324 const double elevationRange = heightMeters * mElevationScale;
1325 mPlot->setYMaximum( mPlot->yMinimum() + elevationRange );
1326 }
1327 }
1328}
1329
1331{
1332 return mElevationScale;
1333}
1334
1336{
1337 mElevationScale = scale;
1338}
1339
1341{
1342 return mDistanceScale;
1343}
1344
1346{
1347 mDistanceScale = scale;
1348}
1349
1354
1356{
1357 mRangeMethod = method;
1358}
@ 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.