QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgsquickelevationprofilecanvas.cpp
Go to the documentation of this file.
1/***************************************************************************
2 QgsQuickElevationProfileCanvas.cpp
3 -----------------
4 begin : October 2022
5 copyright : (C) 2022 by Mathieu Pellerin
6 email : mathieu at opengis dot ch
7***************************************************************************/
8
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
20
26#include "qgsmaplayerutils.h"
27#include "qgsplot.h"
28#include "qgsprofilerenderer.h"
29#include "qgsprofilerequest.h"
31#include "qgsterrainprovider.h"
32
33#include <QQuickWindow>
34#include <QSGSimpleRectNode>
35#include <QSGSimpleTextureNode>
36#include <QScreen>
37#include <QTimer>
38
39#include "moc_qgsquickelevationprofilecanvas.cpp"
40
42class QgsElevationProfilePlotItem : public Qgs2DXyPlot
43{
44 public:
45 explicit QgsElevationProfilePlotItem( QgsQuickElevationProfileCanvas *canvas )
46 : mCanvas( canvas )
47 {
48 setYMinimum( 0 );
49 setYMaximum( 100 );
50 setSize( mCanvas->boundingRect().size() );
51 }
52
53 void setRenderer( QgsProfilePlotRenderer *renderer )
54 {
55 mRenderer = renderer;
56 }
57
58 void updateRect()
59 {
60 setSize( mCanvas->boundingRect().size() );
61 mCachedImages.clear();
62 mPlotArea = QRectF();
63 }
64
65 void updatePlot()
66 {
67 mCachedImages.clear();
68 mPlotArea = QRectF();
69 }
70
71 bool redrawResults( const QString &sourceId )
72 {
73 auto it = mCachedImages.find( sourceId );
74 if ( it == mCachedImages.end() )
75 return false;
76
77 mCachedImages.erase( it );
78 return true;
79 }
80
81 QRectF plotArea()
82 {
83 if ( !mPlotArea.isNull() )
84 return mPlotArea;
85
86 // force immediate recalculation of plot area
87 QgsRenderContext context;
88 context.setScaleFactor( ( mCanvas->window()->screen()->physicalDotsPerInch() * mCanvas->window()->screen()->devicePixelRatio() ) / 25.4 );
89
90 QgsPlotRenderContext plotContext;
91 calculateOptimisedIntervals( context, plotContext );
92 mPlotArea = interiorPlotArea( context, plotContext );
93 return mPlotArea;
94 }
95
96 void renderContent( QgsRenderContext &rc, QgsPlotRenderContext &, const QRectF &plotArea, const QgsPlotData & ) override
97 {
98 mPlotArea = plotArea;
99
100 if ( !mRenderer )
101 return;
102
103 const QStringList sourceIds = mRenderer->sourceIds();
104 for ( const QString &source : sourceIds )
105 {
106 QImage plot;
107 auto it = mCachedImages.constFind( source );
108 if ( it != mCachedImages.constEnd() )
109 {
110 plot = it.value();
111 }
112 else
113 {
114 const float devicePixelRatio = static_cast<float>( mCanvas->window()->screen()->devicePixelRatio() );
115 plot = QImage( static_cast<int>( plotArea.width() * devicePixelRatio ), static_cast<int>( plotArea.height() * devicePixelRatio ), QImage::Format_ARGB32_Premultiplied );
116 plot.setDevicePixelRatio( devicePixelRatio );
117 plot.fill( Qt::transparent );
118
119 QPainter plotPainter( &plot );
120 plotPainter.setRenderHint( QPainter::Antialiasing, true );
121 QgsRenderContext plotRc = QgsRenderContext::fromQPainter( &plotPainter );
122 plotRc.setDevicePixelRatio( devicePixelRatio );
123
124 const double mapUnitsPerPixel = ( xMaximum() - xMinimum() ) / plotArea.width();
125 plotRc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
126
127 mRenderer->render( plotRc, plotArea.width(), plotArea.height(), xMinimum(), xMaximum(), yMinimum(), yMaximum(), source );
128 plotPainter.end();
129
130 mCachedImages.insert( source, plot );
131 }
132 rc.painter()->drawImage( static_cast<int>( plotArea.left() ), static_cast<int>( plotArea.top() ), plot );
133 }
134 }
135
136 private:
137 QgsQuickElevationProfileCanvas *mCanvas = nullptr;
138 QgsProfilePlotRenderer *mRenderer = nullptr;
139
140 QRectF mPlotArea;
141 QMap<QString, QImage> mCachedImages;
142};
144
145
147 : QQuickItem( parent )
148{
149 // updating the profile plot is deferred on a timer, so that we don't trigger it too often
150 mDeferredRegenerationTimer = new QTimer( this );
151 mDeferredRegenerationTimer->setSingleShot( true );
152 mDeferredRegenerationTimer->stop();
153 connect( mDeferredRegenerationTimer, &QTimer::timeout, this, &QgsQuickElevationProfileCanvas::startDeferredRegeneration );
154
155 mDeferredRedrawTimer = new QTimer( this );
156 mDeferredRedrawTimer->setSingleShot( true );
157 mDeferredRedrawTimer->stop();
158 connect( mDeferredRedrawTimer, &QTimer::timeout, this, &QgsQuickElevationProfileCanvas::startDeferredRedraw );
159
160 mPlotItem = new QgsElevationProfilePlotItem( this );
161
162 setTransformOrigin( QQuickItem::TopLeft );
163 setFlags( QQuickItem::ItemHasContents );
164}
165
167{
168 if ( mCurrentJob )
169 {
170 mPlotItem->setRenderer( nullptr );
171 mCurrentJob->deleteLater();
172 mCurrentJob = nullptr;
173 }
174}
175
177{
178 if ( mCurrentJob )
179 {
180 mPlotItem->setRenderer( nullptr );
181 disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
182 mCurrentJob->cancelGeneration();
183 mCurrentJob->deleteLater();
184 mCurrentJob = nullptr;
185 }
186}
187
188void QgsQuickElevationProfileCanvas::setupLayerConnections( QgsMapLayer *layer, bool isDisconnect )
189{
190 if ( !layer )
191 return;
192
193 if ( isDisconnect )
194 {
195 disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
196 disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileRenderingPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
197 disconnect( layer, &QgsMapLayer::dataChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
198 }
199 else
200 {
201 connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
202 connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileRenderingPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
203 connect( layer, &QgsMapLayer::dataChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
204 }
205
206 switch ( layer->type() )
207 {
209 {
210 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
211 if ( isDisconnect )
212 {
213 disconnect( vl, &QgsVectorLayer::featureAdded, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
214 disconnect( vl, &QgsVectorLayer::featureDeleted, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
215 disconnect( vl, &QgsVectorLayer::geometryChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
216 disconnect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
217 }
218 else
219 {
220 connect( vl, &QgsVectorLayer::featureAdded, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
221 connect( vl, &QgsVectorLayer::featureDeleted, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
222 connect( vl, &QgsVectorLayer::geometryChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
223 connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
224 }
225 break;
226 }
235 break;
236 }
237}
238
240{
241 return mCurrentJob && mCurrentJob->isActive();
242}
243
245{
246 if ( !mCrs.isValid() || !mProject || mProfileCurve.isEmpty() )
247 return;
248
249 if ( mCurrentJob )
250 {
251 mPlotItem->setRenderer( nullptr );
252 disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
253 mCurrentJob->deleteLater();
254 mCurrentJob = nullptr;
255 }
256
257 QgsProfileRequest request( static_cast<QgsCurve *>( mProfileCurve.get()->clone() ) );
258 request.setCrs( mCrs );
259 request.setTolerance( mTolerance );
260 request.setTransformContext( mProject->transformContext() );
261 request.setTerrainProvider( mProject->elevationProperties()->terrainProvider() ? mProject->elevationProperties()->terrainProvider()->clone() : nullptr );
262
263 QgsExpressionContext context;
266 request.setExpressionContext( context );
267
268 const QList<QgsMapLayer *> layersToGenerate = layers();
269 QList<QgsAbstractProfileSource *> sources;
270 sources.reserve( layersToGenerate.size() );
271 for ( QgsMapLayer *layer : layersToGenerate )
272 {
273 if ( QgsAbstractProfileSource *source = layer->profileSource() )
274 sources.append( source );
275 }
276
277 mCurrentJob = new QgsProfilePlotRenderer( sources, request );
278 connect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
279
280 QgsProfileGenerationContext generationContext;
281 generationContext.setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
282 generationContext.setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve.get()->length() ) / mPlotItem->plotArea().width() );
283 generationContext.setMapUnitsPerDistancePixel( mProfileCurve.get()->length() / mPlotItem->plotArea().width() );
284 mCurrentJob->setContext( generationContext );
285
286 mPlotItem->updatePlot();
287 mCurrentJob->startGeneration();
288 mPlotItem->setRenderer( mCurrentJob );
289
290 emit activeJobCountChanged( 1 );
291 emit isRenderingChanged();
292}
293
294void QgsQuickElevationProfileCanvas::generationFinished()
295{
296 if ( !mCurrentJob )
297 return;
298
299 emit activeJobCountChanged( 0 );
300
301 if ( mZoomFullWhenJobFinished )
302 {
303 mZoomFullWhenJobFinished = false;
304 zoomFull();
305 }
306
307 QRectF rect = boundingRect();
308 const float devicePixelRatio = static_cast<float>( window()->screen()->devicePixelRatio() );
309 mImage = QImage( static_cast<int>( rect.width() * devicePixelRatio ), static_cast<int>( rect.height() * devicePixelRatio ), QImage::Format_ARGB32_Premultiplied );
310 mImage.setDevicePixelRatio( devicePixelRatio );
311 mImage.fill( Qt::transparent );
312
313 QPainter imagePainter( &mImage );
314 imagePainter.setRenderHint( QPainter::Antialiasing, true );
315 QgsRenderContext rc = QgsRenderContext::fromQPainter( &imagePainter );
316 rc.setDevicePixelRatio( devicePixelRatio );
317
320
321 QgsPlotRenderContext plotContext;
322 mPlotItem->calculateOptimisedIntervals( rc, plotContext );
323 mPlotItem->render( rc, plotContext );
324 imagePainter.end();
325
326 mDirty = true;
327 update();
328
329 if ( mForceRegenerationAfterCurrentJobCompletes )
330 {
331 mForceRegenerationAfterCurrentJobCompletes = false;
332 mCurrentJob->invalidateAllRefinableSources();
333 scheduleDeferredRegeneration();
334 }
335 else
336 {
337 emit isRenderingChanged();
338 }
339}
340
341void QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
342{
343 // TODO -- handle nicely when existing job is in progress
344 if ( !mCurrentJob || mCurrentJob->isActive() )
345 return;
346
347 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
348 if ( !properties )
349 return;
350
351 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
352 {
353 if ( QgsAbstractProfileSource *source = layer->profileSource() )
354 {
355 if ( mCurrentJob->invalidateResults( source ) )
356 scheduleDeferredRegeneration();
357 }
358 }
359}
360
361void QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
362{
363 // TODO -- handle nicely when existing job is in progress
364 if ( !mCurrentJob || mCurrentJob->isActive() )
365 return;
366
367 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
368 if ( !properties )
369 return;
370
371 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
372 {
373 if ( QgsAbstractProfileSource *source = layer->profileSource() )
374 {
375 mCurrentJob->replaceSource( source );
376 }
377 if ( mPlotItem->redrawResults( layer->id() ) )
378 scheduleDeferredRedraw();
379 }
380}
381
382void QgsQuickElevationProfileCanvas::regenerateResultsForLayer()
383{
384 if ( !mCurrentJob )
385 return;
386
387 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
388 {
389 if ( QgsAbstractProfileSource *source = layer->profileSource() )
390 {
391 if ( mCurrentJob->invalidateResults( source ) )
392 scheduleDeferredRegeneration();
393 }
394 }
395}
396
397void QgsQuickElevationProfileCanvas::scheduleDeferredRegeneration()
398{
399 if ( !mDeferredRegenerationScheduled )
400 {
401 mDeferredRegenerationTimer->start( 1 );
402 mDeferredRegenerationScheduled = true;
403 }
404}
405
406void QgsQuickElevationProfileCanvas::scheduleDeferredRedraw()
407{
408 if ( !mDeferredRedrawScheduled )
409 {
410 mDeferredRedrawTimer->start( 1 );
411 mDeferredRedrawScheduled = true;
412 }
413}
414
415void QgsQuickElevationProfileCanvas::startDeferredRegeneration()
416{
417 if ( mCurrentJob && !mCurrentJob->isActive() )
418 {
419 emit activeJobCountChanged( 1 );
420 mCurrentJob->regenerateInvalidatedResults();
421 }
422 else if ( mCurrentJob )
423 {
424 mForceRegenerationAfterCurrentJobCompletes = true;
425 }
426
427 mDeferredRegenerationScheduled = false;
428}
429
430void QgsQuickElevationProfileCanvas::startDeferredRedraw()
431{
432 refresh();
433 mDeferredRedrawScheduled = false;
434}
435
436void QgsQuickElevationProfileCanvas::refineResults()
437{
438 if ( mCurrentJob )
439 {
440 QgsProfileGenerationContext context;
441 context.setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
442 const double plotDistanceRange = mPlotItem->xMaximum() - mPlotItem->xMinimum();
443 const double plotElevationRange = mPlotItem->yMaximum() - mPlotItem->yMinimum();
444 const double plotDistanceUnitsPerPixel = plotDistanceRange / mPlotItem->plotArea().width();
445
446 // we round the actual desired map error down to just one significant figure, to avoid tiny differences
447 // as the plot is panned
448 const double targetMaxErrorInMapUnits = MAX_ERROR_PIXELS * plotDistanceUnitsPerPixel;
449 const double factor = std::pow( 10.0, 1 - std::ceil( std::log10( std::fabs( targetMaxErrorInMapUnits ) ) ) );
450 const double roundedErrorInMapUnits = std::floor( targetMaxErrorInMapUnits * factor ) / factor;
451 context.setMaximumErrorMapUnits( roundedErrorInMapUnits );
452
453 context.setMapUnitsPerDistancePixel( plotDistanceUnitsPerPixel );
454
455 // for similar reasons we round the minimum distance off to multiples of the maximum error in map units
456 const double distanceMin = std::floor( ( mPlotItem->xMinimum() - plotDistanceRange * 0.05 ) / context.maximumErrorMapUnits() ) * context.maximumErrorMapUnits();
457 context.setDistanceRange( QgsDoubleRange( std::max( 0.0, distanceMin ), mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
458
459 context.setElevationRange( QgsDoubleRange( mPlotItem->yMinimum() - plotElevationRange * 0.05, mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
460 mCurrentJob->setContext( context );
461 }
462 scheduleDeferredRegeneration();
463}
464
466{
467 if ( mProject == project )
468 return;
469
470 mProject = project;
471
472 emit projectChanged();
473}
474
476{
477 if ( mCrs == crs )
478 return;
479
480 mCrs = crs;
481
482 emit crsChanged();
483}
484
486{
487 if ( mProfileCurve.equals( curve ) )
488 return;
489
490 mProfileCurve = curve.type() == Qgis::GeometryType::Line ? curve : QgsGeometry();
491
492 emit profileCurveChanged();
493}
494
496{
497 if ( mTolerance == tolerance )
498 return;
499
500 mTolerance = tolerance;
501
502 emit toleranceChanged();
503}
504
506{
507 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
508 {
509 setupLayerConnections( layer, true );
510 }
511
512 if ( !mProject )
513 {
514 mLayers.clear();
515 return;
516 }
517
518 const QList<QgsMapLayer *> projectLayers = QgsProject::instance()->layers<QgsMapLayer *>().toList();
519 // sort layers so that types which are more likely to obscure others are rendered below
520 // e.g. vector features should be drawn above raster DEMS, or the DEM line may completely obscure
521 // the vector feature
523
524 // filter list, removing null layers and invalid layers
525 auto filteredList = sortedLayers;
526 filteredList.erase( std::remove_if( filteredList.begin(), filteredList.end(), []( QgsMapLayer *layer ) {
527 return !layer || !layer->isValid() || !layer->elevationProperties() || !layer->elevationProperties()->showByDefaultInElevationProfilePlots();
528 } ),
529 filteredList.end() );
530
531 mLayers = _qgis_listRawToQPointer( filteredList );
532 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
533 {
534 setupLayerConnections( layer, false );
535 }
536}
537
538QList<QgsMapLayer *> QgsQuickElevationProfileCanvas::layers() const
539{
540 return _qgis_listQPointerToRaw( mLayers );
541}
542
543#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
544void QgsQuickElevationProfileCanvas::geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry )
545{
546 QQuickItem::geometryChanged( newGeometry, oldGeometry );
547#else
548void QgsQuickElevationProfileCanvas::geometryChange( const QRectF &newGeometry, const QRectF &oldGeometry )
549{
550 QQuickItem::geometryChange( newGeometry, oldGeometry );
551#endif
552 mPlotItem->updateRect();
553 mDirty = true;
554 refresh();
555}
556
557QSGNode *QgsQuickElevationProfileCanvas::updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * )
558{
559 if ( mDirty )
560 {
561 delete oldNode;
562 oldNode = nullptr;
563 mDirty = false;
564 }
565
566 QSGNode *newNode = nullptr;
567 if ( !mImage.isNull() )
568 {
569 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>( oldNode );
570 if ( !node )
571 {
572 node = new QSGSimpleTextureNode();
573 QSGTexture *texture = window()->createTextureFromImage( mImage );
574 node->setTexture( texture );
575 node->setOwnsTexture( true );
576 }
577
578 QRectF rect( boundingRect() );
579 QSizeF size = mImage.size();
580 if ( !size.isEmpty() )
581 size /= window()->screen()->devicePixelRatio();
582
583 // Check for resizes that change the w/h ratio
584 if ( !rect.isEmpty() && !size.isEmpty() && !qgsDoubleNear( rect.width() / rect.height(), ( size.width() ) / static_cast<double>( size.height() ), 3 ) )
585 {
586 if ( qgsDoubleNear( rect.height(), mImage.height() ) )
587 {
588 rect.setHeight( rect.width() / size.width() * size.height() );
589 }
590 else
591 {
592 rect.setWidth( rect.height() / size.height() * size.width() );
593 }
594 }
595 node->setRect( rect );
596 newNode = node;
597 }
598 else
599 {
600 QSGSimpleRectNode *node = static_cast<QSGSimpleRectNode *>( oldNode );
601 if ( !node )
602 {
603 node = new QSGSimpleRectNode();
604 node->setColor( Qt::transparent );
605 }
606 node->setRect( boundingRect() );
607 newNode = node;
608 }
609
610 return newNode;
611}
612
614{
615 if ( !mCurrentJob )
616 return;
617
618 const QgsDoubleRange zRange = mCurrentJob->zRange();
619
620 if ( zRange.upper() < zRange.lower() )
621 {
622 // invalid range, e.g. no features found in plot!
623 mPlotItem->setYMinimum( 0 );
624 mPlotItem->setYMaximum( 10 );
625 }
626 else if ( qgsDoubleNear( zRange.lower(), zRange.upper(), 0.0000001 ) )
627 {
628 // corner case ... a zero height plot! Just pick an arbitrary +/- 5 height range.
629 mPlotItem->setYMinimum( zRange.lower() - 5 );
630 mPlotItem->setYMaximum( zRange.lower() + 5 );
631 }
632 else
633 {
634 // add 5% margin to height range
635 const double margin = ( zRange.upper() - zRange.lower() ) * 0.05;
636 mPlotItem->setYMinimum( zRange.lower() - margin );
637 mPlotItem->setYMaximum( zRange.upper() + margin );
638 }
639
640 const double profileLength = mProfileCurve.get()->length();
641 mPlotItem->setXMinimum( 0 );
642 // just 2% margin to max distance -- any more is overkill and wasted space
643 mPlotItem->setXMaximum( profileLength * 1.02 );
644
645 refineResults();
646}
647
649{
650 if ( !mCurrentJob )
651 return;
652
653 const QgsDoubleRange zRange = mCurrentJob->zRange();
654 double xLength = mProfileCurve.get()->length();
655 double yLength = zRange.upper() - zRange.lower();
656 qDebug() << yLength;
657 if ( yLength < 0.0 )
658 {
659 // invalid range, e.g. no features found in plot!
660 mPlotItem->setYMinimum( 0 );
661 mPlotItem->setYMaximum( 10 );
662
663 mPlotItem->setXMinimum( 0 );
664 // just 2% margin to max distance -- any more is overkill and wasted space
665 mPlotItem->setXMaximum( xLength * 1.02 );
666 }
667 else
668 {
669 double yInRatioLength = xLength * mPlotItem->size().height() / mPlotItem->size().width();
670 double xInRatioLength = yLength * mPlotItem->size().width() / mPlotItem->size().height();
671 if ( yInRatioLength > yLength )
672 {
673 qDebug() << "yInRatioLength";
674 mPlotItem->setYMinimum( zRange.lower() - ( yInRatioLength / 2 ) );
675 qDebug() << mPlotItem->yMinimum();
676 mPlotItem->setYMaximum( zRange.upper() + ( yInRatioLength / 2 ) );
677 qDebug() << mPlotItem->yMaximum();
678
679 mPlotItem->setXMinimum( 0 );
680 // just 2% margin to max distance -- any more is overkill and wasted space
681 mPlotItem->setXMaximum( xLength * 1.02 );
682 }
683 else
684 {
685 qDebug() << "xInRatioLength";
686 // add 5% margin to height range
687 const double margin = yLength * 0.05;
688 mPlotItem->setYMinimum( zRange.lower() - margin );
689 qDebug() << mPlotItem->yMinimum();
690 mPlotItem->setYMaximum( zRange.upper() + margin );
691 qDebug() << mPlotItem->yMaximum();
692
693 mPlotItem->setXMinimum( 0 - ( xInRatioLength / 2 ) );
694 mPlotItem->setXMaximum( xLength + ( xInRatioLength / 2 ) );
695 }
696 }
697
698 refineResults();
699}
700
701void QgsQuickElevationProfileCanvas::setVisiblePlotRange( double minimumDistance, double maximumDistance, double minimumElevation, double maximumElevation )
702{
703 mPlotItem->setYMinimum( minimumElevation );
704 mPlotItem->setYMaximum( maximumElevation );
705 mPlotItem->setXMinimum( minimumDistance );
706 mPlotItem->setXMaximum( maximumDistance );
707 refineResults();
708}
709
711{
712 return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
713}
714
716{
717 return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
718}
719
721{
723 if ( mCurrentJob )
724 {
725 mPlotItem->setRenderer( nullptr );
726 disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
727 mCurrentJob->deleteLater();
728 mCurrentJob = nullptr;
729 }
730
731 mZoomFullWhenJobFinished = true;
732
733 mImage = QImage();
734 mDirty = true;
735 update();
736}
@ Line
Lines.
Definition qgis.h:360
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:198
@ Plugin
Plugin based layer.
Definition qgis.h:193
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:199
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:196
@ Vector
Vector layer.
Definition qgis.h:191
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:195
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:194
@ Raster
Raster layer.
Definition qgis.h:192
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:197
virtual void renderContent(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QRectF &plotArea, const QgsPlotData &plotData=QgsPlotData())
Renders the plot content.
Definition qgsplot.cpp:263
void setSize(QSizeF size)
Sets the overall size of the plot (including titles and over components which sit outside the plot ar...
Definition qgsplot.cpp:274
Base class for 2-dimensional plot/chart/graphs with an X and Y axes.
Definition qgsplot.h:659
double yMaximum() const
Returns the maximum value of the y axis.
Definition qgsplot.h:742
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
Definition qgsplot.h:721
double xMinimum() const
Returns the minimum value of the x axis.
Definition qgsplot.h:700
void calculateOptimisedIntervals(QgsRenderContext &context, QgsPlotRenderContext &plotContext)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
Definition qgsplot.cpp:889
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:714
QRectF interiorPlotArea(QgsRenderContext &context, QgsPlotRenderContext &plotContext) const override
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
Definition qgsplot.cpp:760
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
Definition qgsplot.h:749
double xMaximum() const
Returns the maximum value of the x axis.
Definition qgsplot.h:728
Interface for classes which can generate elevation profiles.
Represents a coordinate reference system (CRS).
Abstract base class for curved geometry type.
Definition qgscurve.h:36
QgsRange which stores a range of double values.
Definition qgsrange.h:233
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
A geometry is the spatial representation of a feature.
Qgis::GeometryType type
void profileGenerationPropertyChanged()
Emitted when any of the elevation properties which relate solely to generation of elevation profiles ...
void profileRenderingPropertyChanged()
Emitted when any of the elevation properties which relate solely to presentation of elevation results...
static QList< QgsMapLayer * > sortLayersByType(const QList< QgsMapLayer * > &layers, const QList< Qgis::LayerType > &order)
Sorts a list of map layers by their layer type, respecting the order of types specified.
Base class for all map layer types.
Definition qgsmaplayer.h:80
virtual QgsAbstractProfileSource * profileSource()
Returns the layer's profile source if it has profile capabilities.
QString id
Definition qgsmaplayer.h:83
Qgis::LayerType type
Definition qgsmaplayer.h:90
void dataChanged()
Data of layer changed.
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
Encapsulates the context in which an elevation profile is to be generated.
double maximumErrorMapUnits() const
Returns the maximum allowed error in the generated result, in profile curve map units.
void setDpi(double dpi)
Sets the dpi (dots per inch) for the profie, to be used in size conversions.
void setMaximumErrorMapUnits(double error)
Sets the maximum allowed error in the generated result, in profile curve map units.
void setDistanceRange(const QgsDoubleRange &range)
Sets the range of distances to include in the generation.
void setElevationRange(const QgsDoubleRange &range)
Sets the range of elevations to include in the generation.
void setMapUnitsPerDistancePixel(double units)
Sets the number of map units per pixel in the distance dimension.
Generates and renders elevation profile plots.
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void cancelJobs()
Cancel any rendering job in a blocking way.
void setTolerance(double tolerance)
Sets the profile tolerance (in crs() units).
QgsQuickElevationProfileCanvas(QQuickItem *parent=nullptr)
Constructor for QgsElevationProfileCanvas, with the specified parent widget.
void activeJobCountChanged(int count)
Emitted when the number of active background jobs changes.
void setProfileCurve(QgsGeometry curve)
Sets the profile curve geometry.
void crsChanged()
Emitted when the CRS linked to the profile curve geometry changes.
void setVisiblePlotRange(double minimumDistance, double maximumDistance, double minimumElevation, double maximumElevation)
Sets the visible area of the plot.
bool isRendering
The isRendering property is set to true while a rendering job is pending for this elevation profile c...
void setProject(QgsProject *project)
Sets the project associated with the profile.
void profileCurveChanged()
Emitted when the profile curve geometry changes.
QList< QgsMapLayer * > layers() const
Returns the list of layers included in the profile.
QgsDoubleRange visibleDistanceRange() const
Returns the distance range currently visible in the plot.
Q_INVOKABLE void refresh()
Triggers a complete regeneration of the profile, causing the profile extraction to perform in the bac...
QSGNode * updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) override
QgsDoubleRange visibleElevationRange() const
Returns the elevation range currently visible in the plot.
Q_INVOKABLE void populateLayersFromProject()
Populates the current profile with elevation-enabled layers from the associated project.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs associated with the map coordinates.
Q_INVOKABLE void clear()
Clears the current profile.
void projectChanged()
Emitted when the associated project changes.
void toleranceChanged()
Emitted when the tolerance changes.
Q_INVOKABLE void zoomFull()
Zooms to the full extent of the profile.
void isRenderingChanged()
The isRendering property is set to true while a rendering job is pending for this elevation profile c...
Q_INVOKABLE void zoomFullInRatio()
Zooms to the full extent of the profile while maintaining X and Y axes' length ratio.
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:78
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:85
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
void setDevicePixelRatio(float ratio)
Sets the device pixel ratio.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Emitted whenever an attribute value change is done in the edit buffer.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607