QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 ) { mRenderer = renderer; }
54
55 void updateRect()
56 {
57 setSize( mCanvas->boundingRect().size() );
58 mCachedImages.clear();
59 mPlotArea = QRectF();
60 }
61
62 void updatePlot()
63 {
64 mCachedImages.clear();
65 mPlotArea = QRectF();
66 }
67
68 bool redrawResults( const QString &sourceId )
69 {
70 auto it = mCachedImages.find( sourceId );
71 if ( it == mCachedImages.end() )
72 return false;
73
74 mCachedImages.erase( it );
75 return true;
76 }
77
78 QRectF plotArea()
79 {
80 if ( !mPlotArea.isNull() )
81 return mPlotArea;
82
83 // force immediate recalculation of plot area
84 QgsRenderContext context;
85 context.setScaleFactor( ( mCanvas->window()->screen()->physicalDotsPerInch() * mCanvas->window()->screen()->devicePixelRatio() ) / 25.4 );
86
87 QgsPlotRenderContext plotContext;
88 calculateOptimisedIntervals( context, plotContext );
89 mPlotArea = interiorPlotArea( context, plotContext );
90 return mPlotArea;
91 }
92
93 void renderContent( QgsRenderContext &rc, QgsPlotRenderContext &, const QRectF &plotArea, const QgsPlotData & ) override
94 {
95 mPlotArea = plotArea;
96
97 if ( !mRenderer )
98 return;
99
100 const QStringList sourceIds = mRenderer->sourceIds();
101 for ( const QString &source : sourceIds )
102 {
103 QImage plot;
104 auto it = mCachedImages.constFind( source );
105 if ( it != mCachedImages.constEnd() )
106 {
107 plot = it.value();
108 }
109 else
110 {
111 const float devicePixelRatio = static_cast<float>( mCanvas->window()->screen()->devicePixelRatio() );
112 plot = QImage( static_cast<int>( plotArea.width() * devicePixelRatio ), static_cast<int>( plotArea.height() * devicePixelRatio ), QImage::Format_ARGB32_Premultiplied );
113 plot.setDevicePixelRatio( devicePixelRatio );
114 plot.fill( Qt::transparent );
115
116 QPainter plotPainter( &plot );
117 plotPainter.setRenderHint( QPainter::Antialiasing, true );
118 QgsRenderContext plotRc = QgsRenderContext::fromQPainter( &plotPainter );
119 plotRc.setDevicePixelRatio( devicePixelRatio );
120
121 const double mapUnitsPerPixel = ( xMaximum() - xMinimum() ) / plotArea.width();
122 plotRc.setMapToPixel( QgsMapToPixel( mapUnitsPerPixel ) );
123
124 mRenderer->render( plotRc, plotArea.width(), plotArea.height(), xMinimum(), xMaximum(), yMinimum(), yMaximum(), source );
125 plotPainter.end();
126
127 mCachedImages.insert( source, plot );
128 }
129 rc.painter()->drawImage( static_cast<int>( plotArea.left() ), static_cast<int>( plotArea.top() ), plot );
130 }
131 }
132
133 private:
134 QgsQuickElevationProfileCanvas *mCanvas = nullptr;
135 QgsProfilePlotRenderer *mRenderer = nullptr;
136
137 QRectF mPlotArea;
138 QMap<QString, QImage> mCachedImages;
139};
141
142
144 : QQuickItem( parent )
145{
146 // updating the profile plot is deferred on a timer, so that we don't trigger it too often
147 mDeferredRegenerationTimer = new QTimer( this );
148 mDeferredRegenerationTimer->setSingleShot( true );
149 mDeferredRegenerationTimer->stop();
150 connect( mDeferredRegenerationTimer, &QTimer::timeout, this, &QgsQuickElevationProfileCanvas::startDeferredRegeneration );
151
152 mDeferredRedrawTimer = new QTimer( this );
153 mDeferredRedrawTimer->setSingleShot( true );
154 mDeferredRedrawTimer->stop();
155 connect( mDeferredRedrawTimer, &QTimer::timeout, this, &QgsQuickElevationProfileCanvas::startDeferredRedraw );
156
157 mPlotItem = new QgsElevationProfilePlotItem( this );
158
159 setTransformOrigin( QQuickItem::TopLeft );
160 setFlags( QQuickItem::ItemHasContents );
161}
162
164{
165 if ( mCurrentJob )
166 {
167 mPlotItem->setRenderer( nullptr );
168 mCurrentJob->deleteLater();
169 mCurrentJob = nullptr;
170 }
171}
172
174{
175 if ( mCurrentJob )
176 {
177 mPlotItem->setRenderer( nullptr );
178 disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
179 mCurrentJob->cancelGeneration();
180 mCurrentJob->deleteLater();
181 mCurrentJob = nullptr;
182 }
183}
184
185void QgsQuickElevationProfileCanvas::setupLayerConnections( QgsMapLayer *layer, bool isDisconnect )
186{
187 if ( !layer )
188 return;
189
190 if ( isDisconnect )
191 {
192 disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
193 disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileRenderingPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
194 disconnect( layer, &QgsMapLayer::dataChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
195 }
196 else
197 {
198 connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
199 connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileRenderingPropertyChanged, this, &QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
200 connect( layer, &QgsMapLayer::dataChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
201 }
202
203 switch ( layer->type() )
204 {
206 {
207 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
208 if ( isDisconnect )
209 {
210 disconnect( vl, &QgsVectorLayer::featureAdded, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
211 disconnect( vl, &QgsVectorLayer::featureDeleted, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
212 disconnect( vl, &QgsVectorLayer::geometryChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
213 disconnect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
214 }
215 else
216 {
217 connect( vl, &QgsVectorLayer::featureAdded, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
218 connect( vl, &QgsVectorLayer::featureDeleted, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
219 connect( vl, &QgsVectorLayer::geometryChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
220 connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsQuickElevationProfileCanvas::regenerateResultsForLayer );
221 }
222 break;
223 }
232 break;
233 }
234}
235
237{
238 return mCurrentJob && mCurrentJob->isActive();
239}
240
242{
243 if ( !mCrs.isValid() || !mProject || mProfileCurve.isEmpty() )
244 return;
245
246 if ( mCurrentJob )
247 {
248 mPlotItem->setRenderer( nullptr );
249 disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
250 mCurrentJob->deleteLater();
251 mCurrentJob = nullptr;
252 }
253
254 QgsProfileRequest request( static_cast<QgsCurve *>( mProfileCurve.get()->clone() ) );
255 request.setCrs( mCrs );
256 request.setTolerance( mTolerance );
257 request.setTransformContext( mProject->transformContext() );
258 request.setTerrainProvider( mProject->elevationProperties()->terrainProvider() ? mProject->elevationProperties()->terrainProvider()->clone() : nullptr );
259
260 QgsExpressionContext context;
263 request.setExpressionContext( context );
264
265 const QList<QgsMapLayer *> layersToGenerate = layers();
266 QList<QgsAbstractProfileSource *> sources;
267 sources.reserve( layersToGenerate.size() );
268 for ( QgsMapLayer *layer : layersToGenerate )
269 {
270 if ( QgsAbstractProfileSource *source = layer->profileSource() )
271 sources.append( source );
272 }
273
274 mCurrentJob = new QgsProfilePlotRenderer( sources, request );
275 connect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
276
277 QgsProfileGenerationContext generationContext;
278 generationContext.setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
279 generationContext.setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve.get()->length() ) / mPlotItem->plotArea().width() );
280 generationContext.setMapUnitsPerDistancePixel( mProfileCurve.get()->length() / mPlotItem->plotArea().width() );
281 mCurrentJob->setContext( generationContext );
282
283 mPlotItem->updatePlot();
284 mCurrentJob->startGeneration();
285 mPlotItem->setRenderer( mCurrentJob );
286
287 emit activeJobCountChanged( 1 );
288 emit isRenderingChanged();
289}
290
291void QgsQuickElevationProfileCanvas::generationFinished()
292{
293 if ( !mCurrentJob )
294 return;
295
296 emit activeJobCountChanged( 0 );
297
298 if ( mZoomFullWhenJobFinished )
299 {
300 mZoomFullWhenJobFinished = false;
301 zoomFull();
302 }
303
304 QRectF rect = boundingRect();
305 const float devicePixelRatio = static_cast<float>( window()->screen()->devicePixelRatio() );
306 mImage = QImage( static_cast<int>( rect.width() * devicePixelRatio ), static_cast<int>( rect.height() * devicePixelRatio ), QImage::Format_ARGB32_Premultiplied );
307 mImage.setDevicePixelRatio( devicePixelRatio );
308 mImage.fill( Qt::transparent );
309
310 QPainter imagePainter( &mImage );
311 imagePainter.setRenderHint( QPainter::Antialiasing, true );
312 QgsRenderContext rc = QgsRenderContext::fromQPainter( &imagePainter );
313 rc.setDevicePixelRatio( devicePixelRatio );
314
317
318 QgsPlotRenderContext plotContext;
319 mPlotItem->calculateOptimisedIntervals( rc, plotContext );
320 mPlotItem->render( rc, plotContext );
321 imagePainter.end();
322
323 mDirty = true;
324 update();
325
326 if ( mForceRegenerationAfterCurrentJobCompletes )
327 {
328 mForceRegenerationAfterCurrentJobCompletes = false;
329 mCurrentJob->invalidateAllRefinableSources();
330 scheduleDeferredRegeneration();
331 }
332 else
333 {
334 emit isRenderingChanged();
335 }
336}
337
338void QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
339{
340 // TODO -- handle nicely when existing job is in progress
341 if ( !mCurrentJob || mCurrentJob->isActive() )
342 return;
343
344 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
345 if ( !properties )
346 return;
347
348 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
349 {
350 if ( QgsAbstractProfileSource *source = layer->profileSource() )
351 {
352 if ( mCurrentJob->invalidateResults( source ) )
353 scheduleDeferredRegeneration();
354 }
355 }
356}
357
358void QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
359{
360 // TODO -- handle nicely when existing job is in progress
361 if ( !mCurrentJob || mCurrentJob->isActive() )
362 return;
363
364 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
365 if ( !properties )
366 return;
367
368 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
369 {
370 if ( QgsAbstractProfileSource *source = layer->profileSource() )
371 {
372 mCurrentJob->replaceSource( source );
373 }
374 if ( mPlotItem->redrawResults( layer->id() ) )
375 scheduleDeferredRedraw();
376 }
377}
378
379void QgsQuickElevationProfileCanvas::regenerateResultsForLayer()
380{
381 if ( !mCurrentJob )
382 return;
383
384 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
385 {
386 if ( QgsAbstractProfileSource *source = layer->profileSource() )
387 {
388 if ( mCurrentJob->invalidateResults( source ) )
389 scheduleDeferredRegeneration();
390 }
391 }
392}
393
394void QgsQuickElevationProfileCanvas::scheduleDeferredRegeneration()
395{
396 if ( !mDeferredRegenerationScheduled )
397 {
398 mDeferredRegenerationTimer->start( 1 );
399 mDeferredRegenerationScheduled = true;
400 }
401}
402
403void QgsQuickElevationProfileCanvas::scheduleDeferredRedraw()
404{
405 if ( !mDeferredRedrawScheduled )
406 {
407 mDeferredRedrawTimer->start( 1 );
408 mDeferredRedrawScheduled = true;
409 }
410}
411
412void QgsQuickElevationProfileCanvas::startDeferredRegeneration()
413{
414 if ( mCurrentJob && !mCurrentJob->isActive() )
415 {
416 emit activeJobCountChanged( 1 );
417 mCurrentJob->regenerateInvalidatedResults();
418 }
419 else if ( mCurrentJob )
420 {
421 mForceRegenerationAfterCurrentJobCompletes = true;
422 }
423
424 mDeferredRegenerationScheduled = false;
425}
426
427void QgsQuickElevationProfileCanvas::startDeferredRedraw()
428{
429 refresh();
430 mDeferredRedrawScheduled = false;
431}
432
433void QgsQuickElevationProfileCanvas::refineResults()
434{
435 if ( mCurrentJob )
436 {
437 QgsProfileGenerationContext context;
438 context.setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
439 const double plotDistanceRange = mPlotItem->xMaximum() - mPlotItem->xMinimum();
440 const double plotElevationRange = mPlotItem->yMaximum() - mPlotItem->yMinimum();
441 const double plotDistanceUnitsPerPixel = plotDistanceRange / mPlotItem->plotArea().width();
442
443 // we round the actual desired map error down to just one significant figure, to avoid tiny differences
444 // as the plot is panned
445 const double targetMaxErrorInMapUnits = MAX_ERROR_PIXELS * plotDistanceUnitsPerPixel;
446 const double factor = std::pow( 10.0, 1 - std::ceil( std::log10( std::fabs( targetMaxErrorInMapUnits ) ) ) );
447 const double roundedErrorInMapUnits = std::floor( targetMaxErrorInMapUnits * factor ) / factor;
448 context.setMaximumErrorMapUnits( roundedErrorInMapUnits );
449
450 context.setMapUnitsPerDistancePixel( plotDistanceUnitsPerPixel );
451
452 // for similar reasons we round the minimum distance off to multiples of the maximum error in map units
453 const double distanceMin = std::floor( ( mPlotItem->xMinimum() - plotDistanceRange * 0.05 ) / context.maximumErrorMapUnits() ) * context.maximumErrorMapUnits();
454 context.setDistanceRange( QgsDoubleRange( std::max( 0.0, distanceMin ), mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
455
456 context.setElevationRange( QgsDoubleRange( mPlotItem->yMinimum() - plotElevationRange * 0.05, mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
457 mCurrentJob->setContext( context );
458 }
459 scheduleDeferredRegeneration();
460}
461
463{
464 if ( mProject == project )
465 return;
466
467 mProject = project;
468
469 emit projectChanged();
470}
471
473{
474 if ( mCrs == crs )
475 return;
476
477 mCrs = crs;
478
479 emit crsChanged();
480}
481
483{
484 if ( mProfileCurve.equals( curve ) )
485 return;
486
487 mProfileCurve = curve.type() == Qgis::GeometryType::Line ? curve : QgsGeometry();
488
489 emit profileCurveChanged();
490}
491
493{
494 if ( mTolerance == tolerance )
495 return;
496
497 mTolerance = tolerance;
498
499 emit toleranceChanged();
500}
501
503{
504 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
505 {
506 setupLayerConnections( layer, true );
507 }
508
509 if ( !mProject )
510 {
511 mLayers.clear();
512 return;
513 }
514
515 const QList<QgsMapLayer *> projectLayers = QgsProject::instance()->layers<QgsMapLayer *>().toList();
516 // sort layers so that types which are more likely to obscure others are rendered below
517 // e.g. vector features should be drawn above raster DEMS, or the DEM line may completely obscure
518 // the vector feature
520
521 // filter list, removing null layers and invalid layers
522 auto filteredList = sortedLayers;
523 filteredList.erase(
524 std::remove_if(
525 filteredList.begin(),
526 filteredList.end(),
527 []( QgsMapLayer *layer ) { return !layer || !layer->isValid() || !layer->elevationProperties() || !layer->elevationProperties()->showByDefaultInElevationProfilePlots(); }
528 ),
529 filteredList.end()
530 );
531
532 mLayers = _qgis_listRawToQPointer( filteredList );
533 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
534 {
535 setupLayerConnections( layer, false );
536 }
537}
538
539QList<QgsMapLayer *> QgsQuickElevationProfileCanvas::layers() const
540{
541 return _qgis_listQPointerToRaw( mLayers );
542}
543
544void QgsQuickElevationProfileCanvas::geometryChange( const QRectF &newGeometry, const QRectF &oldGeometry )
545{
546 QQuickItem::geometryChange( newGeometry, oldGeometry );
547 mPlotItem->updateRect();
548 mDirty = true;
549 refresh();
550}
551
552QSGNode *QgsQuickElevationProfileCanvas::updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * )
553{
554 if ( mDirty )
555 {
556 delete oldNode;
557 oldNode = nullptr;
558 mDirty = false;
559 }
560
561 QSGNode *newNode = nullptr;
562 if ( !mImage.isNull() )
563 {
564 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>( oldNode );
565 if ( !node )
566 {
567 node = new QSGSimpleTextureNode();
568 QSGTexture *texture = window()->createTextureFromImage( mImage );
569 node->setTexture( texture );
570 node->setOwnsTexture( true );
571 }
572
573 QRectF rect( boundingRect() );
574 QSizeF size = mImage.size();
575 if ( !size.isEmpty() )
576 size /= window()->screen()->devicePixelRatio();
577
578 // Check for resizes that change the w/h ratio
579 if ( !rect.isEmpty() && !size.isEmpty() && !qgsDoubleNear( rect.width() / rect.height(), ( size.width() ) / static_cast<double>( size.height() ), 3 ) )
580 {
581 if ( qgsDoubleNear( rect.height(), mImage.height() ) )
582 {
583 rect.setHeight( rect.width() / size.width() * size.height() );
584 }
585 else
586 {
587 rect.setWidth( rect.height() / size.height() * size.width() );
588 }
589 }
590 node->setRect( rect );
591 newNode = node;
592 }
593 else
594 {
595 QSGSimpleRectNode *node = static_cast<QSGSimpleRectNode *>( oldNode );
596 if ( !node )
597 {
598 node = new QSGSimpleRectNode();
599 node->setColor( Qt::transparent );
600 }
601 node->setRect( boundingRect() );
602 newNode = node;
603 }
604
605 return newNode;
606}
607
609{
610 if ( !mCurrentJob )
611 return;
612
613 const QgsDoubleRange zRange = mCurrentJob->zRange();
614
615 if ( zRange.upper() < zRange.lower() )
616 {
617 // invalid range, e.g. no features found in plot!
618 mPlotItem->setYMinimum( 0 );
619 mPlotItem->setYMaximum( 10 );
620 }
621 else if ( qgsDoubleNear( zRange.lower(), zRange.upper(), 0.0000001 ) )
622 {
623 // corner case ... a zero height plot! Just pick an arbitrary +/- 5 height range.
624 mPlotItem->setYMinimum( zRange.lower() - 5 );
625 mPlotItem->setYMaximum( zRange.lower() + 5 );
626 }
627 else
628 {
629 // add 5% margin to height range
630 const double margin = ( zRange.upper() - zRange.lower() ) * 0.05;
631 mPlotItem->setYMinimum( zRange.lower() - margin );
632 mPlotItem->setYMaximum( zRange.upper() + margin );
633 }
634
635 const double profileLength = mProfileCurve.get()->length();
636 mPlotItem->setXMinimum( 0 );
637 // just 2% margin to max distance -- any more is overkill and wasted space
638 mPlotItem->setXMaximum( profileLength * 1.02 );
639
640 refineResults();
641}
642
644{
645 if ( !mCurrentJob )
646 return;
647
648 const QgsDoubleRange zRange = mCurrentJob->zRange();
649 double xLength = mProfileCurve.get()->length();
650 double yLength = zRange.upper() - zRange.lower();
651 qDebug() << yLength;
652 if ( yLength < 0.0 )
653 {
654 // invalid range, e.g. no features found in plot!
655 mPlotItem->setYMinimum( 0 );
656 mPlotItem->setYMaximum( 10 );
657
658 mPlotItem->setXMinimum( 0 );
659 // just 2% margin to max distance -- any more is overkill and wasted space
660 mPlotItem->setXMaximum( xLength * 1.02 );
661 }
662 else
663 {
664 double yInRatioLength = xLength * mPlotItem->size().height() / mPlotItem->size().width();
665 double xInRatioLength = yLength * mPlotItem->size().width() / mPlotItem->size().height();
666 if ( yInRatioLength > yLength )
667 {
668 qDebug() << "yInRatioLength";
669 mPlotItem->setYMinimum( zRange.lower() - ( yInRatioLength / 2 ) );
670 qDebug() << mPlotItem->yMinimum();
671 mPlotItem->setYMaximum( zRange.upper() + ( yInRatioLength / 2 ) );
672 qDebug() << mPlotItem->yMaximum();
673
674 mPlotItem->setXMinimum( 0 );
675 // just 2% margin to max distance -- any more is overkill and wasted space
676 mPlotItem->setXMaximum( xLength * 1.02 );
677 }
678 else
679 {
680 qDebug() << "xInRatioLength";
681 // add 5% margin to height range
682 const double margin = yLength * 0.05;
683 mPlotItem->setYMinimum( zRange.lower() - margin );
684 qDebug() << mPlotItem->yMinimum();
685 mPlotItem->setYMaximum( zRange.upper() + margin );
686 qDebug() << mPlotItem->yMaximum();
687
688 mPlotItem->setXMinimum( 0 - ( xInRatioLength / 2 ) );
689 mPlotItem->setXMaximum( xLength + ( xInRatioLength / 2 ) );
690 }
691 }
692
693 refineResults();
694}
695
696void QgsQuickElevationProfileCanvas::setVisiblePlotRange( double minimumDistance, double maximumDistance, double minimumElevation, double maximumElevation )
697{
698 mPlotItem->setYMinimum( minimumElevation );
699 mPlotItem->setYMaximum( maximumElevation );
700 mPlotItem->setXMinimum( minimumDistance );
701 mPlotItem->setXMaximum( maximumDistance );
702 refineResults();
703}
704
706{
707 return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
708}
709
711{
712 return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
713}
714
716{
718 if ( mCurrentJob )
719 {
720 mPlotItem->setRenderer( nullptr );
721 disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsQuickElevationProfileCanvas::generationFinished );
722 mCurrentJob->deleteLater();
723 mCurrentJob = nullptr;
724 }
725
726 mZoomFullWhenJobFinished = true;
727
728 mImage = QImage();
729 mDirty = true;
730 update();
731}
@ Line
Lines.
Definition qgis.h:381
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:214
@ Plugin
Plugin based layer.
Definition qgis.h:209
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
virtual void renderContent(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QRectF &plotArea, const QgsPlotData &plotData=QgsPlotData())
Renders the plot content.
Definition qgsplot.cpp:310
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:320
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
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
Definition qgsplot.h:748
double xMinimum() const
Returns the minimum value of the x axis.
Definition qgsplot.h:727
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:1042
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:741
QRectF interiorPlotArea(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData=QgsPlotData()) const override
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
Definition qgsplot.cpp:871
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
Definition qgsplot.h:776
double xMaximum() const
Returns the maximum value of the x axis.
Definition qgsplot.h:755
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:217
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:83
virtual QgsAbstractProfileSource * profileSource()
Returns the layer's profile source if it has profile capabilities.
QString id
Definition qgsmaplayer.h:86
Qgis::LayerType type
Definition qgsmaplayer.h:93
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:113
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:79
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:86
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:6975