33#include <QQuickWindow>
34#include <QSGSimpleRectNode>
35#include <QSGSimpleTextureNode>
39#include "moc_qgsquickelevationprofilecanvas.cpp"
42class QgsElevationProfilePlotItem :
public Qgs2DXyPlot
45 explicit QgsElevationProfilePlotItem( QgsQuickElevationProfileCanvas *canvas )
50 setSize( mCanvas->boundingRect().size() );
53 void setRenderer( QgsProfilePlotRenderer *renderer )
60 setSize( mCanvas->boundingRect().size() );
61 mCachedImages.clear();
67 mCachedImages.clear();
71 bool redrawResults(
const QString &sourceId )
73 auto it = mCachedImages.find( sourceId );
74 if ( it == mCachedImages.end() )
77 mCachedImages.erase( it );
83 if ( !mPlotArea.isNull() )
87 QgsRenderContext context;
88 context.
setScaleFactor( ( mCanvas->window()->screen()->physicalDotsPerInch() * mCanvas->window()->screen()->devicePixelRatio() ) / 25.4 );
90 QgsPlotRenderContext plotContext;
96 void renderContent( QgsRenderContext &rc, QgsPlotRenderContext &,
const QRectF &plotArea,
const QgsPlotData & )
override
103 const QStringList sourceIds = mRenderer->sourceIds();
104 for (
const QString &source : sourceIds )
107 auto it = mCachedImages.constFind( source );
108 if ( it != mCachedImages.constEnd() )
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 );
119 QPainter plotPainter( &plot );
120 plotPainter.setRenderHint( QPainter::Antialiasing,
true );
124 const double mapUnitsPerPixel = (
xMaximum() -
xMinimum() ) / plotArea.width();
127 mRenderer->render( plotRc, plotArea.width(), plotArea.height(), xMinimum(),
xMaximum(),
yMinimum(),
yMaximum(), source );
130 mCachedImages.insert( source, plot );
132 rc.
painter()->drawImage(
static_cast<int>( plotArea.left() ),
static_cast<int>( plotArea.top() ), plot );
137 QgsQuickElevationProfileCanvas *mCanvas =
nullptr;
138 QgsProfilePlotRenderer *mRenderer =
nullptr;
141 QMap<QString, QImage> mCachedImages;
147 : QQuickItem( parent )
150 mDeferredRegenerationTimer =
new QTimer(
this );
151 mDeferredRegenerationTimer->setSingleShot(
true );
152 mDeferredRegenerationTimer->stop();
153 connect( mDeferredRegenerationTimer, &QTimer::timeout,
this, &QgsQuickElevationProfileCanvas::startDeferredRegeneration );
155 mDeferredRedrawTimer =
new QTimer(
this );
156 mDeferredRedrawTimer->setSingleShot(
true );
157 mDeferredRedrawTimer->stop();
158 connect( mDeferredRedrawTimer, &QTimer::timeout,
this, &QgsQuickElevationProfileCanvas::startDeferredRedraw );
160 mPlotItem =
new QgsElevationProfilePlotItem(
this );
162 setTransformOrigin( QQuickItem::TopLeft );
163 setFlags( QQuickItem::ItemHasContents );
170 mPlotItem->setRenderer(
nullptr );
171 mCurrentJob->deleteLater();
172 mCurrentJob =
nullptr;
180 mPlotItem->setRenderer(
nullptr );
182 mCurrentJob->cancelGeneration();
183 mCurrentJob->deleteLater();
184 mCurrentJob =
nullptr;
188void QgsQuickElevationProfileCanvas::setupLayerConnections(
QgsMapLayer *layer,
bool isDisconnect )
206 switch ( layer->
type() )
210 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
241 return mCurrentJob && mCurrentJob->isActive();
246 if ( !mCrs.isValid() || !mProject || mProfileCurve.isEmpty() )
251 mPlotItem->setRenderer(
nullptr );
253 mCurrentJob->deleteLater();
254 mCurrentJob =
nullptr;
261 request.
setTerrainProvider( mProject->elevationProperties()->terrainProvider() ? mProject->elevationProperties()->terrainProvider()->clone() :
nullptr );
268 const QList<QgsMapLayer *> layersToGenerate =
layers();
269 QList<QgsAbstractProfileSource *> sources;
270 sources.reserve( layersToGenerate.size() );
274 sources.append( source );
281 generationContext.
setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
282 generationContext.
setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve.get()->length() ) / mPlotItem->plotArea().width() );
284 mCurrentJob->setContext( generationContext );
286 mPlotItem->updatePlot();
287 mCurrentJob->startGeneration();
288 mPlotItem->setRenderer( mCurrentJob );
294void QgsQuickElevationProfileCanvas::generationFinished()
301 if ( mZoomFullWhenJobFinished )
303 mZoomFullWhenJobFinished =
false;
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 );
313 QPainter imagePainter( &mImage );
314 imagePainter.setRenderHint( QPainter::Antialiasing,
true );
321 QgsPlotRenderContext plotContext;
322 mPlotItem->calculateOptimisedIntervals( rc, plotContext );
323 mPlotItem->render( rc, plotContext );
329 if ( mForceRegenerationAfterCurrentJobCompletes )
331 mForceRegenerationAfterCurrentJobCompletes =
false;
332 mCurrentJob->invalidateAllRefinableSources();
333 scheduleDeferredRegeneration();
341void QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
344 if ( !mCurrentJob || mCurrentJob->isActive() )
347 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
351 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
353 if ( QgsAbstractProfileSource *source = layer->
profileSource() )
355 if ( mCurrentJob->invalidateResults( source ) )
356 scheduleDeferredRegeneration();
361void QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
364 if ( !mCurrentJob || mCurrentJob->isActive() )
367 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
371 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
373 if ( QgsAbstractProfileSource *source = layer->
profileSource() )
375 mCurrentJob->replaceSource( source );
377 if ( mPlotItem->redrawResults( layer->
id() ) )
378 scheduleDeferredRedraw();
382void QgsQuickElevationProfileCanvas::regenerateResultsForLayer()
387 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
389 if ( QgsAbstractProfileSource *source = layer->
profileSource() )
391 if ( mCurrentJob->invalidateResults( source ) )
392 scheduleDeferredRegeneration();
397void QgsQuickElevationProfileCanvas::scheduleDeferredRegeneration()
399 if ( !mDeferredRegenerationScheduled )
401 mDeferredRegenerationTimer->start( 1 );
402 mDeferredRegenerationScheduled =
true;
406void QgsQuickElevationProfileCanvas::scheduleDeferredRedraw()
408 if ( !mDeferredRedrawScheduled )
410 mDeferredRedrawTimer->start( 1 );
411 mDeferredRedrawScheduled =
true;
415void QgsQuickElevationProfileCanvas::startDeferredRegeneration()
417 if ( mCurrentJob && !mCurrentJob->isActive() )
420 mCurrentJob->regenerateInvalidatedResults();
422 else if ( mCurrentJob )
424 mForceRegenerationAfterCurrentJobCompletes =
true;
427 mDeferredRegenerationScheduled =
false;
430void QgsQuickElevationProfileCanvas::startDeferredRedraw()
433 mDeferredRedrawScheduled =
false;
436void QgsQuickElevationProfileCanvas::refineResults()
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();
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;
457 context.
setDistanceRange( QgsDoubleRange( std::max( 0.0, distanceMin ), mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
459 context.
setElevationRange( QgsDoubleRange( mPlotItem->yMinimum() - plotElevationRange * 0.05, mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
460 mCurrentJob->setContext( context );
462 scheduleDeferredRegeneration();
487 if ( mProfileCurve.equals( curve ) )
507 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
509 setupLayerConnections( layer,
true );
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();
529 filteredList.end() );
531 mLayers = _qgis_listRawToQPointer( filteredList );
532 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
534 setupLayerConnections( layer,
false );
540 return _qgis_listQPointerToRaw( mLayers );
543#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
544void QgsQuickElevationProfileCanvas::geometryChanged(
const QRectF &newGeometry,
const QRectF &oldGeometry )
546 QQuickItem::geometryChanged( newGeometry, oldGeometry );
550 QQuickItem::geometryChange( newGeometry, oldGeometry );
552 mPlotItem->updateRect();
566 QSGNode *newNode =
nullptr;
567 if ( !mImage.isNull() )
569 QSGSimpleTextureNode *node =
static_cast<QSGSimpleTextureNode *
>( oldNode );
572 node =
new QSGSimpleTextureNode();
573 QSGTexture *texture = window()->createTextureFromImage( mImage );
574 node->setTexture( texture );
575 node->setOwnsTexture(
true );
578 QRectF rect( boundingRect() );
579 QSizeF size = mImage.size();
580 if ( !size.isEmpty() )
581 size /= window()->screen()->devicePixelRatio();
584 if ( !rect.isEmpty() && !size.isEmpty() && !
qgsDoubleNear( rect.width() / rect.height(), ( size.width() ) /
static_cast<double>( size.height() ), 3 ) )
588 rect.setHeight( rect.width() / size.width() * size.height() );
592 rect.setWidth( rect.height() / size.height() * size.width() );
595 node->setRect( rect );
600 QSGSimpleRectNode *node =
static_cast<QSGSimpleRectNode *
>( oldNode );
603 node =
new QSGSimpleRectNode();
604 node->setColor( Qt::transparent );
606 node->setRect( boundingRect() );
623 mPlotItem->setYMinimum( 0 );
624 mPlotItem->setYMaximum( 10 );
629 mPlotItem->setYMinimum( zRange.
lower() - 5 );
630 mPlotItem->setYMaximum( zRange.
lower() + 5 );
635 const double margin = ( zRange.
upper() - zRange.
lower() ) * 0.05;
636 mPlotItem->setYMinimum( zRange.
lower() - margin );
637 mPlotItem->setYMaximum( zRange.
upper() + margin );
640 const double profileLength = mProfileCurve.get()->length();
641 mPlotItem->setXMinimum( 0 );
643 mPlotItem->setXMaximum( profileLength * 1.02 );
654 double xLength = mProfileCurve.get()->length();
655 double yLength = zRange.
upper() - zRange.
lower();
660 mPlotItem->setYMinimum( 0 );
661 mPlotItem->setYMaximum( 10 );
663 mPlotItem->setXMinimum( 0 );
665 mPlotItem->setXMaximum( xLength * 1.02 );
669 double yInRatioLength = xLength * mPlotItem->size().height() / mPlotItem->size().width();
670 double xInRatioLength = yLength * mPlotItem->size().width() / mPlotItem->size().height();
671 if ( yInRatioLength > yLength )
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();
679 mPlotItem->setXMinimum( 0 );
681 mPlotItem->setXMaximum( xLength * 1.02 );
685 qDebug() <<
"xInRatioLength";
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();
693 mPlotItem->setXMinimum( 0 - ( xInRatioLength / 2 ) );
694 mPlotItem->setXMaximum( xLength + ( xInRatioLength / 2 ) );
703 mPlotItem->setYMinimum( minimumElevation );
704 mPlotItem->setYMaximum( maximumElevation );
705 mPlotItem->setXMinimum( minimumDistance );
706 mPlotItem->setXMaximum( maximumDistance );
712 return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
717 return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
725 mPlotItem->setRenderer(
nullptr );
727 mCurrentJob->deleteLater();
728 mCurrentJob =
nullptr;
731 mZoomFullWhenJobFinished =
true;
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
virtual void renderContent(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QRectF &plotArea, const QgsPlotData &plotData=QgsPlotData())
Renders the plot content.
void setSize(QSizeF size)
Sets the overall size of the plot (including titles and over components which sit outside the plot ar...
Base class for 2-dimensional plot/chart/graphs with an X and Y axes.
double yMaximum() const
Returns the maximum value of the y axis.
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
double xMinimum() const
Returns the minimum value of the x axis.
void calculateOptimisedIntervals(QgsRenderContext &context, QgsPlotRenderContext &plotContext)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
double yMinimum() const
Returns the minimum value of the y axis.
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...
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
double xMaximum() const
Returns the maximum value of the x axis.
Interface for classes which can generate elevation profiles.
Represents a coordinate reference system (CRS).
Abstract base class for curved geometry type.
QgsRange which stores a range of double values.
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.
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.
virtual QgsAbstractProfileSource * profileSource()
Returns the layer's profile source if it has profile capabilities.
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,...
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()
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.
QgsCoordinateReferenceSystem crs
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.
T upper() const
Returns the upper bound of the range.
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).