32#include <QQuickWindow> 
   33#include <QSGSimpleRectNode> 
   34#include <QSGSimpleTextureNode> 
   40class QgsElevationProfilePlotItem : 
public Qgs2DPlot 
   48      setSize( mCanvas->boundingRect().size() );
 
   58      setSize( mCanvas->boundingRect().size() );
 
   59      mCachedImages.clear();
 
   65      mCachedImages.clear();
 
   69    bool redrawResults( 
const QString &sourceId )
 
   71      auto it = mCachedImages.find( sourceId );
 
   72      if ( it == mCachedImages.end() )
 
   75      mCachedImages.erase( it );
 
   81      if ( !mPlotArea.isNull() )
 
   86      context.
setScaleFactor( ( mCanvas->window()->screen()->physicalDotsPerInch() * mCanvas->window()->screen()->devicePixelRatio() ) / 25.4 );
 
  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();
 
  127          mCachedImages.insert( source, plot );
 
  129        rc.
painter()->drawImage( 
static_cast<int>( plotArea.left() ), 
static_cast<int>( plotArea.top() ), plot );
 
  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 );
 
  180    mCurrentJob->deleteLater();
 
  181    mCurrentJob = 
nullptr;
 
  185void QgsQuickElevationProfileCanvas::setupLayerConnections( 
QgsMapLayer *layer, 
bool isDisconnect )
 
  203  switch ( layer->
type() )
 
  205    case Qgis::LayerType::Vector:
 
  224    case Qgis::LayerType::Raster:
 
  225    case Qgis::LayerType::Plugin:
 
  226    case Qgis::LayerType::Mesh:
 
  227    case Qgis::LayerType::VectorTile:
 
  228    case Qgis::LayerType::Annotation:
 
  229    case Qgis::LayerType::PointCloud:
 
  230    case Qgis::LayerType::Group:
 
  237  return mCurrentJob && mCurrentJob->
isActive();
 
  247    mPlotItem->setRenderer( 
nullptr );
 
  249    mCurrentJob->deleteLater();
 
  250    mCurrentJob = 
nullptr;
 
  264  const QList<QgsMapLayer *> layersToGenerate = 
layers();
 
  265  QList<QgsAbstractProfileSource *> sources;
 
  266  sources.reserve( layersToGenerate.size() );
 
  270      sources.append( source );
 
  277  generationContext.
setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
 
  282  mPlotItem->updatePlot();
 
  284  mPlotItem->setRenderer( mCurrentJob );
 
  290void QgsQuickElevationProfileCanvas::generationFinished()
 
  297  if ( mZoomFullWhenJobFinished )
 
  299    mZoomFullWhenJobFinished = 
false;
 
  303  QRectF rect = boundingRect();
 
  304  const float devicePixelRatio = 
static_cast<float>( window()->screen()->devicePixelRatio() );
 
  305  mImage = QImage( 
static_cast<int>( rect.width() * devicePixelRatio ), 
static_cast<int>( rect.height() * devicePixelRatio ), QImage::Format_ARGB32_Premultiplied );
 
  306  mImage.setDevicePixelRatio( devicePixelRatio );
 
  307  mImage.fill( Qt::transparent );
 
  309  QPainter imagePainter( &mImage );
 
  310  imagePainter.setRenderHint( QPainter::Antialiasing, 
true );
 
  317  mPlotItem->calculateOptimisedIntervals( rc );
 
  318  mPlotItem->render( rc );
 
  324  if ( mForceRegenerationAfterCurrentJobCompletes )
 
  326    mForceRegenerationAfterCurrentJobCompletes = 
false;
 
  328    scheduleDeferredRegeneration();
 
  336void QgsQuickElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
 
  339  if ( !mCurrentJob || mCurrentJob->
isActive() )
 
  346  if ( 
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
 
  351        scheduleDeferredRegeneration();
 
  356void QgsQuickElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
 
  359  if ( !mCurrentJob || mCurrentJob->
isActive() )
 
  366  if ( 
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( properties->parent() ) )
 
  372    if ( mPlotItem->redrawResults( layer->
id() ) )
 
  373      scheduleDeferredRedraw();
 
  377void QgsQuickElevationProfileCanvas::regenerateResultsForLayer()
 
  382  if ( 
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
 
  387        scheduleDeferredRegeneration();
 
  392void QgsQuickElevationProfileCanvas::scheduleDeferredRegeneration()
 
  394  if ( !mDeferredRegenerationScheduled )
 
  396    mDeferredRegenerationTimer->start( 1 );
 
  397    mDeferredRegenerationScheduled = 
true;
 
  401void QgsQuickElevationProfileCanvas::scheduleDeferredRedraw()
 
  403  if ( !mDeferredRedrawScheduled )
 
  405    mDeferredRedrawTimer->start( 1 );
 
  406    mDeferredRedrawScheduled = 
true;
 
  410void QgsQuickElevationProfileCanvas::startDeferredRegeneration()
 
  412  if ( mCurrentJob && !mCurrentJob->
isActive() )
 
  417  else if ( mCurrentJob )
 
  419    mForceRegenerationAfterCurrentJobCompletes = 
true;
 
  422  mDeferredRegenerationScheduled = 
false;
 
  425void QgsQuickElevationProfileCanvas::startDeferredRedraw()
 
  428  mDeferredRedrawScheduled = 
false;
 
  431void QgsQuickElevationProfileCanvas::refineResults()
 
  436    context.
setDpi( window()->screen()->physicalDotsPerInch() * window()->screen()->devicePixelRatio() );
 
  437    const double plotDistanceRange = mPlotItem->xMaximum() - mPlotItem->xMinimum();
 
  438    const double plotElevationRange = mPlotItem->yMaximum() - mPlotItem->yMinimum();
 
  439    const double plotDistanceUnitsPerPixel = plotDistanceRange / mPlotItem->plotArea().width();
 
  443    const double targetMaxErrorInMapUnits = MAX_ERROR_PIXELS * plotDistanceUnitsPerPixel;
 
  444    const double factor = std::pow( 10.0, 1 - std::ceil( std::log10( std::fabs( targetMaxErrorInMapUnits ) ) ) );
 
  445    const double roundedErrorInMapUnits = std::floor( targetMaxErrorInMapUnits * factor ) / factor;
 
  453                              mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
 
  456                               mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
 
  459  scheduleDeferredRegeneration();
 
  484  if ( mProfileCurve.
equals( curve ) )
 
  487  mProfileCurve = curve.
type() == Qgis::GeometryType::Line ? curve : 
QgsGeometry();
 
  504  for ( 
QgsMapLayer *layer : std::as_const( mLayers ) )
 
  506    setupLayerConnections( layer, 
true );
 
  521    Qgis::LayerType::Raster,
 
  522    Qgis::LayerType::Mesh,
 
  523    Qgis::LayerType::Vector,
 
  524    Qgis::LayerType::PointCloud
 
  528  auto filteredList = sortedLayers;
 
  529  filteredList.erase( std::remove_if( filteredList.begin(), filteredList.end(),
 
  532    return !layer || !layer->isValid() || !layer->elevationProperties() || !layer->elevationProperties()->showByDefaultInElevationProfilePlots();
 
  534  filteredList.end() );
 
  536  mLayers = _qgis_listRawToQPointer( filteredList );
 
  537  for ( 
QgsMapLayer *layer : std::as_const( mLayers ) )
 
  539    setupLayerConnections( layer, 
false );
 
  545  return _qgis_listQPointerToRaw( mLayers );
 
  548#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) 
  549void QgsQuickElevationProfileCanvas::geometryChanged( 
const QRectF &newGeometry, 
const QRectF &oldGeometry )
 
  551  QQuickItem::geometryChanged( newGeometry, oldGeometry );
 
  555  QQuickItem::geometryChange( newGeometry, oldGeometry );
 
  557  mPlotItem->updateRect();
 
  571  QSGNode *newNode = 
nullptr;
 
  572  if ( !mImage.isNull() )
 
  574    QSGSimpleTextureNode *node = 
static_cast<QSGSimpleTextureNode *
>( oldNode );
 
  577      node = 
new QSGSimpleTextureNode();
 
  578      QSGTexture *texture = window()->createTextureFromImage( mImage );
 
  579      node->setTexture( texture );
 
  580      node->setOwnsTexture( 
true );
 
  583    QRectF rect( boundingRect() );
 
  584    QSizeF size = mImage.size();
 
  585    if ( !size.isEmpty() )
 
  586      size /= window()->screen()->devicePixelRatio();
 
  589    if ( !rect.isEmpty() && !size.isEmpty() && !
qgsDoubleNear( rect.width() / rect.height(), ( size.width() ) / 
static_cast<double>( size.height() ), 3 ) )
 
  593        rect.setHeight( rect.width() / size.width() * size.height() );
 
  597        rect.setWidth( rect.height() / size.height() * size.width() );
 
  600    node->setRect( rect );
 
  605    QSGSimpleRectNode *node = 
static_cast<QSGSimpleRectNode *
>( oldNode );
 
  608      node = 
new QSGSimpleRectNode();
 
  609      node->setColor( Qt::transparent );
 
  611    node->setRect( boundingRect() );
 
  628    mPlotItem->setYMinimum( 0 );
 
  629    mPlotItem->setYMaximum( 10 );
 
  634    mPlotItem->setYMinimum( zRange.
lower() - 5 );
 
  635    mPlotItem->setYMaximum( zRange.
lower() + 5 );
 
  640    const double margin = ( zRange.
upper() - zRange.
lower() ) * 0.05;
 
  641    mPlotItem->setYMinimum( zRange.
lower() - margin );
 
  642    mPlotItem->setYMaximum( zRange.
upper() + margin );
 
  645  const double profileLength = mProfileCurve.
get()->
length();
 
  646  mPlotItem->setXMinimum( 0 );
 
  648  mPlotItem->setXMaximum( profileLength * 1.02 );
 
  659  double xLength = mProfileCurve.
get()->
length();
 
  660  double yLength = zRange.
upper() - zRange.
lower();
 
  665    mPlotItem->setYMinimum( 0 );
 
  666    mPlotItem->setYMaximum( 10 );
 
  668    mPlotItem->setXMinimum( 0 );
 
  670    mPlotItem->setXMaximum( xLength * 1.02 );
 
  674    double yInRatioLength = xLength * mPlotItem->size().height() / mPlotItem->size().width();
 
  675    double xInRatioLength = yLength * mPlotItem->size().width() / mPlotItem->size().height();
 
  676    if ( yInRatioLength > yLength )
 
  678      qDebug() << 
"yInRatioLength";
 
  679      mPlotItem->setYMinimum( zRange.
lower() - ( yInRatioLength / 2 ) );
 
  680      qDebug() << mPlotItem->yMinimum();
 
  681      mPlotItem->setYMaximum( zRange.
upper() + ( yInRatioLength / 2 ) );
 
  682      qDebug() << mPlotItem->yMaximum();
 
  684      mPlotItem->setXMinimum( 0 );
 
  686      mPlotItem->setXMaximum( xLength * 1.02 );
 
  690      qDebug() << 
"xInRatioLength";
 
  692      const double margin = yLength * 0.05;
 
  693      mPlotItem->setYMinimum( zRange.
lower() - margin );
 
  694      qDebug() << mPlotItem->yMinimum();
 
  695      mPlotItem->setYMaximum( zRange.
upper() + margin );
 
  696      qDebug() << mPlotItem->yMaximum();
 
  698      mPlotItem->setXMinimum( 0 - ( xInRatioLength / 2 ) );
 
  699      mPlotItem->setXMaximum( xLength + ( xInRatioLength / 2 ) );
 
  708  mPlotItem->setYMinimum( minimumElevation );
 
  709  mPlotItem->setYMaximum( maximumElevation );
 
  710  mPlotItem->setXMinimum( minimumDistance );
 
  711  mPlotItem->setXMaximum( maximumDistance );
 
  717  return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
 
  722  return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
 
  730    mPlotItem->setRenderer( 
nullptr );
 
  732    mCurrentJob->deleteLater();
 
  733    mCurrentJob = 
nullptr;
 
  736  mZoomFullWhenJobFinished = 
true;
 
Base class for 2-dimensional plot/chart/graphs.
 
void calculateOptimisedIntervals(QgsRenderContext &context)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
 
double yMaximum() const
Returns the maximum value of the y axis.
 
void setSize(QSizeF size)
Sets the overall size of the plot (including titles and over components which sit outside the plot ar...
 
double xMaximum() const
Returns the maximum value of the x axis.
 
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
 
double xMinimum() const
Returns the minimum value of the x axis.
 
double yMinimum() const
Returns the minimum value of the y axis.
 
QRectF interiorPlotArea(QgsRenderContext &context) const
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
 
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
 
virtual void renderContent(QgsRenderContext &context, const QRectF &plotArea)
Renders the plot content.
 
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
 
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
 
Interface for classes which can generate elevation profiles.
 
virtual QgsAbstractTerrainProvider * clone() const =0
Creates a clone of the provider and returns the new object.
 
This class represents a coordinate reference system (CRS).
 
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
 
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.
 
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
 
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
 
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
 
Base class for storage of map layer elevation properties.
 
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.
 
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
 
void dataChanged()
Data of layer changed.
 
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
 
Perform transforms between map coordinates and device coordinates.
 
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 regenerateInvalidatedResults()
Starts a background regeneration of any invalidated results and immediately returns.
 
void invalidateAllRefinableSources()
Invalidates previous results from all refinable sources.
 
void cancelGeneration()
Stop the generation job - does not return until the job has terminated.
 
void startGeneration()
Start the generation job and immediately return.
 
QgsDoubleRange zRange() const
Returns the limits of the retrieved elevation values.
 
bool isActive() const
Returns true if the generation job is currently running in background.
 
bool invalidateResults(QgsAbstractProfileSource *source)
Invalidates the profile results from the source with matching ID.
 
void replaceSource(QgsAbstractProfileSource *source)
Replaces the existing source with matching ID.
 
void setContext(const QgsProfileGenerationContext &context)
Sets the context in which the profile generation will occur.
 
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.
 
QgsAbstractTerrainProvider * terrainProvider()
Returns the project's terrain provider.
 
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
 
static QgsProject * instance()
Returns the QgsProject singleton instance.
 
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
 
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
 
QgsCoordinateTransformContext transformContext
 
This class implements a visual Qt Quick Item that does elevation profile rendering according to the c...
 
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.
 
Contains information about the context of a rendering operation.
 
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.
 
Represents a vector layer which manages a vector based data sets.
 
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)
 
const QgsCoordinateReferenceSystem & crs