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 ) { mRenderer = renderer; }
57 setSize( mCanvas->boundingRect().size() );
58 mCachedImages.clear();
64 mCachedImages.clear();
68 bool redrawResults(
const QString &sourceId )
70 auto it = mCachedImages.find( sourceId );
71 if ( it == mCachedImages.end() )
74 mCachedImages.erase( it );
80 if ( !mPlotArea.isNull() )
84 QgsRenderContext context;
85 context.
setScaleFactor( ( mCanvas->window()->screen()->physicalDotsPerInch() * mCanvas->window()->screen()->devicePixelRatio() ) / 25.4 );
87 QgsPlotRenderContext plotContext;
93 void renderContent( QgsRenderContext &rc, QgsPlotRenderContext &,
const QRectF &plotArea,
const QgsPlotData & )
override
100 const QStringList sourceIds = mRenderer->sourceIds();
101 for (
const QString &source : sourceIds )
104 auto it = mCachedImages.constFind( source );
105 if ( it != mCachedImages.constEnd() )
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 );
116 QPainter plotPainter( &plot );
117 plotPainter.setRenderHint( QPainter::Antialiasing,
true );
121 const double mapUnitsPerPixel = (
xMaximum() -
xMinimum() ) / plotArea.width();
124 mRenderer->render( plotRc, plotArea.width(), plotArea.height(), xMinimum(),
xMaximum(),
yMinimum(),
yMaximum(), source );
127 mCachedImages.insert( source, plot );
129 rc.
painter()->drawImage(
static_cast<int>( plotArea.left() ),
static_cast<int>( plotArea.top() ), plot );
134 QgsQuickElevationProfileCanvas *mCanvas =
nullptr;
135 QgsProfilePlotRenderer *mRenderer =
nullptr;
138 QMap<QString, QImage> mCachedImages;
144 : QQuickItem( parent )
147 mDeferredRegenerationTimer =
new QTimer(
this );
148 mDeferredRegenerationTimer->setSingleShot(
true );
149 mDeferredRegenerationTimer->stop();
150 connect( mDeferredRegenerationTimer, &QTimer::timeout,
this, &QgsQuickElevationProfileCanvas::startDeferredRegeneration );
152 mDeferredRedrawTimer =
new QTimer(
this );
153 mDeferredRedrawTimer->setSingleShot(
true );
154 mDeferredRedrawTimer->stop();
155 connect( mDeferredRedrawTimer, &QTimer::timeout,
this, &QgsQuickElevationProfileCanvas::startDeferredRedraw );
157 mPlotItem =
new QgsElevationProfilePlotItem(
this );
159 setTransformOrigin( QQuickItem::TopLeft );
160 setFlags( QQuickItem::ItemHasContents );
167 mPlotItem->setRenderer(
nullptr );
168 mCurrentJob->deleteLater();
169 mCurrentJob =
nullptr;
177 mPlotItem->setRenderer(
nullptr );
179 mCurrentJob->cancelGeneration();
180 mCurrentJob->deleteLater();
181 mCurrentJob =
nullptr;
185void QgsQuickElevationProfileCanvas::setupLayerConnections(
QgsMapLayer *layer,
bool isDisconnect )
203 switch ( layer->
type() )
207 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
238 return mCurrentJob && mCurrentJob->isActive();
243 if ( !mCrs.isValid() || !mProject || mProfileCurve.isEmpty() )
248 mPlotItem->setRenderer(
nullptr );
250 mCurrentJob->deleteLater();
251 mCurrentJob =
nullptr;
258 request.
setTerrainProvider( mProject->elevationProperties()->terrainProvider() ? mProject->elevationProperties()->terrainProvider()->clone() :
nullptr );
265 const QList<QgsMapLayer *> layersToGenerate =
layers();
266 QList<QgsAbstractProfileSource *> sources;
267 sources.reserve( layersToGenerate.size() );
271 sources.append( source );
278 generationContext.
setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
279 generationContext.
setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve.get()->length() ) / mPlotItem->plotArea().width() );
281 mCurrentJob->setContext( generationContext );
283 mPlotItem->updatePlot();
284 mCurrentJob->startGeneration();
285 mPlotItem->setRenderer( mCurrentJob );
291void QgsQuickElevationProfileCanvas::generationFinished()
298 if ( mZoomFullWhenJobFinished )
300 mZoomFullWhenJobFinished =
false;
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 );
310 QPainter imagePainter( &mImage );
311 imagePainter.setRenderHint( QPainter::Antialiasing,
true );
318 QgsPlotRenderContext plotContext;
319 mPlotItem->calculateOptimisedIntervals( rc, plotContext );
320 mPlotItem->render( rc, plotContext );
326 if ( mForceRegenerationAfterCurrentJobCompletes )
328 mForceRegenerationAfterCurrentJobCompletes =
false;
329 mCurrentJob->invalidateAllRefinableSources();
330 scheduleDeferredRegeneration();
338void QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
341 if ( !mCurrentJob || mCurrentJob->isActive() )
344 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
348 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
350 if ( QgsAbstractProfileSource *source = layer->
profileSource() )
352 if ( mCurrentJob->invalidateResults( source ) )
353 scheduleDeferredRegeneration();
358void QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
361 if ( !mCurrentJob || mCurrentJob->isActive() )
364 QgsMapLayerElevationProperties *properties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
368 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
370 if ( QgsAbstractProfileSource *source = layer->
profileSource() )
372 mCurrentJob->replaceSource( source );
374 if ( mPlotItem->redrawResults( layer->
id() ) )
375 scheduleDeferredRedraw();
379void QgsQuickElevationProfileCanvas::regenerateResultsForLayer()
384 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
386 if ( QgsAbstractProfileSource *source = layer->
profileSource() )
388 if ( mCurrentJob->invalidateResults( source ) )
389 scheduleDeferredRegeneration();
394void QgsQuickElevationProfileCanvas::scheduleDeferredRegeneration()
396 if ( !mDeferredRegenerationScheduled )
398 mDeferredRegenerationTimer->start( 1 );
399 mDeferredRegenerationScheduled =
true;
403void QgsQuickElevationProfileCanvas::scheduleDeferredRedraw()
405 if ( !mDeferredRedrawScheduled )
407 mDeferredRedrawTimer->start( 1 );
408 mDeferredRedrawScheduled =
true;
412void QgsQuickElevationProfileCanvas::startDeferredRegeneration()
414 if ( mCurrentJob && !mCurrentJob->isActive() )
417 mCurrentJob->regenerateInvalidatedResults();
419 else if ( mCurrentJob )
421 mForceRegenerationAfterCurrentJobCompletes =
true;
424 mDeferredRegenerationScheduled =
false;
427void QgsQuickElevationProfileCanvas::startDeferredRedraw()
430 mDeferredRedrawScheduled =
false;
433void QgsQuickElevationProfileCanvas::refineResults()
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();
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;
454 context.
setDistanceRange( QgsDoubleRange( std::max( 0.0, distanceMin ), mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
456 context.
setElevationRange( QgsDoubleRange( mPlotItem->yMinimum() - plotElevationRange * 0.05, mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
457 mCurrentJob->setContext( context );
459 scheduleDeferredRegeneration();
484 if ( mProfileCurve.equals( curve ) )
504 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
506 setupLayerConnections( layer,
true );
522 auto filteredList = sortedLayers;
525 filteredList.begin(),
527 [](
QgsMapLayer *layer ) { return !layer || !layer->isValid() || !layer->elevationProperties() || !layer->elevationProperties()->showByDefaultInElevationProfilePlots(); }
532 mLayers = _qgis_listRawToQPointer( filteredList );
533 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
535 setupLayerConnections( layer,
false );
541 return _qgis_listQPointerToRaw( mLayers );
546 QQuickItem::geometryChange( newGeometry, oldGeometry );
547 mPlotItem->updateRect();
561 QSGNode *newNode =
nullptr;
562 if ( !mImage.isNull() )
564 QSGSimpleTextureNode *node =
static_cast<QSGSimpleTextureNode *
>( oldNode );
567 node =
new QSGSimpleTextureNode();
568 QSGTexture *texture = window()->createTextureFromImage( mImage );
569 node->setTexture( texture );
570 node->setOwnsTexture(
true );
573 QRectF rect( boundingRect() );
574 QSizeF size = mImage.size();
575 if ( !size.isEmpty() )
576 size /= window()->screen()->devicePixelRatio();
579 if ( !rect.isEmpty() && !size.isEmpty() && !
qgsDoubleNear( rect.width() / rect.height(), ( size.width() ) /
static_cast<double>( size.height() ), 3 ) )
583 rect.setHeight( rect.width() / size.width() * size.height() );
587 rect.setWidth( rect.height() / size.height() * size.width() );
590 node->setRect( rect );
595 QSGSimpleRectNode *node =
static_cast<QSGSimpleRectNode *
>( oldNode );
598 node =
new QSGSimpleRectNode();
599 node->setColor( Qt::transparent );
601 node->setRect( boundingRect() );
618 mPlotItem->setYMinimum( 0 );
619 mPlotItem->setYMaximum( 10 );
624 mPlotItem->setYMinimum( zRange.
lower() - 5 );
625 mPlotItem->setYMaximum( zRange.
lower() + 5 );
630 const double margin = ( zRange.
upper() - zRange.
lower() ) * 0.05;
631 mPlotItem->setYMinimum( zRange.
lower() - margin );
632 mPlotItem->setYMaximum( zRange.
upper() + margin );
635 const double profileLength = mProfileCurve.get()->length();
636 mPlotItem->setXMinimum( 0 );
638 mPlotItem->setXMaximum( profileLength * 1.02 );
649 double xLength = mProfileCurve.get()->length();
650 double yLength = zRange.
upper() - zRange.
lower();
655 mPlotItem->setYMinimum( 0 );
656 mPlotItem->setYMaximum( 10 );
658 mPlotItem->setXMinimum( 0 );
660 mPlotItem->setXMaximum( xLength * 1.02 );
664 double yInRatioLength = xLength * mPlotItem->size().height() / mPlotItem->size().width();
665 double xInRatioLength = yLength * mPlotItem->size().width() / mPlotItem->size().height();
666 if ( yInRatioLength > yLength )
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();
674 mPlotItem->setXMinimum( 0 );
676 mPlotItem->setXMaximum( xLength * 1.02 );
680 qDebug() <<
"xInRatioLength";
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();
688 mPlotItem->setXMinimum( 0 - ( xInRatioLength / 2 ) );
689 mPlotItem->setXMaximum( xLength + ( xInRatioLength / 2 ) );
698 mPlotItem->setYMinimum( minimumElevation );
699 mPlotItem->setYMaximum( maximumElevation );
700 mPlotItem->setXMinimum( minimumDistance );
701 mPlotItem->setXMaximum( maximumDistance );
707 return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
712 return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
720 mPlotItem->setRenderer(
nullptr );
722 mCurrentJob->deleteLater();
723 mCurrentJob =
nullptr;
726 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 QgsPlotData &plotData=QgsPlotData()) 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(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.
~QgsQuickElevationProfileCanvas() override
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).