QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgselevationprofilecanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgselevationprofilecanvas.cpp
3  -----------------
4  begin : March 2022
5  copyright : (C) 2022 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
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 #include "qgsmaplayerlistutils_p.h"
21 #include "qgsplotcanvasitem.h"
22 #include "qgsprofilerequest.h"
24 #include "qgscurve.h"
26 #include "qgsterrainprovider.h"
28 #include "qgsprofilerenderer.h"
29 #include "qgspoint.h"
30 #include "qgsgeos.h"
31 #include "qgsplot.h"
32 #include "qgsguiutils.h"
33 #include "qgsnumericformat.h"
35 #include "qgsprofilesnapping.h"
37 #include "qgsapplication.h"
38 
39 #include <QWheelEvent>
40 #include <QTimer>
41 #include <QDesktopWidget>
42 
44 class QgsElevationProfilePlotItem : public Qgs2DPlot, public QgsPlotCanvasItem
45 {
46  public:
47 
48  QgsElevationProfilePlotItem( QgsElevationProfileCanvas *canvas )
49  : QgsPlotCanvasItem( canvas )
50  {
51  setYMinimum( 0 );
52  setYMaximum( 100 );
53  }
54 
55  void setRenderer( QgsProfilePlotRenderer *renderer )
56  {
57  mRenderer = renderer;
58  }
59 
60  void updateRect()
61  {
62  mRect = mCanvas->rect();
63  setSize( mRect.size() );
64 
65  prepareGeometryChange();
66  setPos( mRect.topLeft() );
67 
68  mImage = QImage();
69  mCachedImages.clear();
70  mPlotArea = QRectF();
71  update();
72  }
73 
74  void updatePlot()
75  {
76  mImage = QImage();
77  mCachedImages.clear();
78  mPlotArea = QRectF();
79  update();
80  }
81 
82  bool redrawResults( const QString &sourceId )
83  {
84  auto it = mCachedImages.find( sourceId );
85  if ( it == mCachedImages.end() )
86  return false;
87 
88  mCachedImages.erase( it );
89  mImage = QImage();
90  return true;
91  }
92 
93  QRectF boundingRect() const override
94  {
95  return mRect;
96  }
97 
98  QRectF plotArea()
99  {
100  if ( !mPlotArea.isNull() )
101  return mPlotArea;
102 
103  // force immediate recalculation of plot area
104  QgsRenderContext context;
105  if ( !scene()->views().isEmpty() )
106  context.setScaleFactor( scene()->views().at( 0 )->logicalDpiX() / 25.4 );
107 
108  calculateOptimisedIntervals( context );
109  mPlotArea = interiorPlotArea( context );
110  return mPlotArea;
111  }
112 
113  QgsProfilePoint canvasPointToPlotPoint( QPointF point )
114  {
115  const QRectF area = plotArea();
116  if ( !area.contains( point.x(), point.y() ) )
117  return QgsProfilePoint();
118 
119  const double distance = ( point.x() - area.left() ) / area.width() * ( xMaximum() - xMinimum() ) + xMinimum();
120  const double elevation = ( area.bottom() - point.y() ) / area.height() * ( yMaximum() - yMinimum() ) + yMinimum();
121  return QgsProfilePoint( distance, elevation );
122  }
123 
124  QgsPointXY plotPointToCanvasPoint( const QgsProfilePoint &point )
125  {
126  if ( point.distance() < xMinimum() || point.distance() > xMaximum() || point.elevation() < yMinimum() || point.elevation() > yMaximum() )
127  return QgsPointXY();
128 
129  const QRectF area = plotArea();
130 
131  const double x = ( point.distance() - xMinimum() ) / ( xMaximum() - xMinimum() ) * ( area.width() ) + area.left();
132  const double y = area.bottom() - ( point.elevation() - yMinimum() ) / ( yMaximum() - yMinimum() ) * ( area.height() );
133  return QgsPointXY( x, y );
134  }
135 
136  void renderContent( QgsRenderContext &rc, const QRectF &plotArea ) override
137  {
138  mPlotArea = plotArea;
139 
140  if ( !mRenderer )
141  return;
142 
143  const QStringList sourceIds = mRenderer->sourceIds();
144  for ( const QString &source : sourceIds )
145  {
146  QImage plot;
147  auto it = mCachedImages.constFind( source );
148  if ( it != mCachedImages.constEnd() )
149  {
150  plot = it.value();
151  }
152  else
153  {
154  plot = mRenderer->renderToImage( plotArea.width(), plotArea.height(), xMinimum(), xMaximum(), yMinimum(), yMaximum(), source );
155  mCachedImages.insert( source, plot );
156  }
157  rc.painter()->drawImage( plotArea.left(), plotArea.top(), plot );
158  }
159  }
160 
161  void paint( QPainter *painter ) override
162  {
163  // cache rendering to an image, so we don't need to redraw the plot
164  if ( !mImage.isNull() )
165  {
166  painter->drawImage( 0, 0, mImage );
167  }
168  else
169  {
170  mImage = QImage( mRect.width(), mRect.height(), QImage::Format_ARGB32_Premultiplied );
171  mImage.fill( Qt::transparent );
172 
173  QPainter imagePainter( &mImage );
174  imagePainter.setRenderHint( QPainter::Antialiasing, true );
175  QgsRenderContext rc = QgsRenderContext::fromQPainter( &imagePainter );
176 
179 
181  render( rc );
182  imagePainter.end();
183 
184  painter->drawImage( 0, 0, mImage );
185  }
186  }
187 
188  QgsProject *mProject = nullptr;
189 
190  private:
191 
192  QImage mImage;
193 
194  QMap< QString, QImage > mCachedImages;
195 
196  QRectF mRect;
197  QRectF mPlotArea;
198  QgsProfilePlotRenderer *mRenderer = nullptr;
199 };
200 
201 class QgsElevationProfileCrossHairsItem : public QgsPlotCanvasItem
202 {
203  public:
204 
205  QgsElevationProfileCrossHairsItem( QgsElevationProfileCanvas *canvas, QgsElevationProfilePlotItem *plotItem )
206  : QgsPlotCanvasItem( canvas )
207  , mPlotItem( plotItem )
208  {
209  }
210 
211  void updateRect()
212  {
213  mRect = mCanvas->rect();
214 
215  prepareGeometryChange();
216  setPos( mRect.topLeft() );
217  update();
218  }
219 
220  void setPoint( const QgsProfilePoint &point )
221  {
222  mPoint = point;
223  update();
224  }
225 
226  QRectF boundingRect() const override
227  {
228  return mRect;
229  }
230 
231  void paint( QPainter *painter ) override
232  {
233  const QgsPointXY crossHairPlotPoint = mPlotItem->plotPointToCanvasPoint( mPoint );
234  if ( crossHairPlotPoint.isEmpty() )
235  return;
236 
237  painter->save();
238  painter->setBrush( Qt::NoBrush );
239  QPen crossHairPen;
240  crossHairPen.setCosmetic( true );
241  crossHairPen.setWidthF( 1 );
242  crossHairPen.setStyle( Qt::DashLine );
243  crossHairPen.setCapStyle( Qt::FlatCap );
244  crossHairPen.setColor( QColor( 0, 0, 0, 150 ) );
245  painter->setPen( crossHairPen );
246  painter->drawLine( QPointF( mPlotItem->plotArea().left(), crossHairPlotPoint.y() ), QPointF( mPlotItem->plotArea().right(), crossHairPlotPoint.y() ) );
247  painter->drawLine( QPointF( crossHairPlotPoint.x(), mPlotItem->plotArea().top() ), QPointF( crossHairPlotPoint.x(), mPlotItem->plotArea().bottom() ) );
248 
249  // also render current point text
250  QgsNumericFormatContext numericContext;
251 
252  const QString xCoordinateText = mPlotItem->xAxis().numericFormat()->formatDouble( mPoint.distance(), numericContext );
253  const QString yCoordinateText = mPlotItem->yAxis().numericFormat()->formatDouble( mPoint.elevation(), numericContext );
254 
255  QFont font;
256  const QFontMetrics fm( font );
257  const double height = fm.capHeight();
258  const double xWidth = fm.horizontalAdvance( xCoordinateText );
259  const double yWidth = fm.horizontalAdvance( yCoordinateText );
260  const double textAxisMargin = fm.horizontalAdvance( ' ' );
261 
262  QPointF xCoordOrigin;
263  QPointF yCoordOrigin;
264 
265  if ( mPoint.distance() < ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5 )
266  {
267  if ( mPoint.elevation() < ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5 )
268  {
269  // render x coordinate on right top (left top align)
270  xCoordOrigin = QPointF( crossHairPlotPoint.x() + textAxisMargin, mPlotItem->plotArea().top() + height + textAxisMargin );
271  // render y coordinate on right top (right bottom align)
272  yCoordOrigin = QPointF( mPlotItem->plotArea().right() - yWidth - textAxisMargin, crossHairPlotPoint.y() - textAxisMargin );
273  }
274  else
275  {
276  // render x coordinate on right bottom (left bottom align)
277  xCoordOrigin = QPointF( crossHairPlotPoint.x() + textAxisMargin, mPlotItem->plotArea().bottom() - textAxisMargin );
278  // render y coordinate on right bottom (right top align)
279  yCoordOrigin = QPointF( mPlotItem->plotArea().right() - yWidth - textAxisMargin, crossHairPlotPoint.y() + height + textAxisMargin );
280  }
281  }
282  else
283  {
284  if ( mPoint.elevation() < ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5 )
285  {
286  // render x coordinate on left top (right top align)
287  xCoordOrigin = QPointF( crossHairPlotPoint.x() - xWidth - textAxisMargin, mPlotItem->plotArea().top() + height + textAxisMargin );
288  // render y coordinate on left top (left bottom align)
289  yCoordOrigin = QPointF( mPlotItem->plotArea().left() + textAxisMargin, crossHairPlotPoint.y() - textAxisMargin );
290  }
291  else
292  {
293  // render x coordinate on left bottom (right bottom align)
294  xCoordOrigin = QPointF( crossHairPlotPoint.x() - xWidth - textAxisMargin, mPlotItem->plotArea().bottom() - textAxisMargin );
295  // render y coordinate on left bottom (left top align)
296  yCoordOrigin = QPointF( mPlotItem->plotArea().left() + textAxisMargin, crossHairPlotPoint.y() + height + textAxisMargin );
297  }
298  }
299 
300  // semi opaque white background
301  painter->setBrush( QBrush( QColor( 255, 255, 255, 220 ) ) );
302  painter->setPen( Qt::NoPen );
303  painter->drawRect( QRectF( xCoordOrigin.x() - textAxisMargin + 1, xCoordOrigin.y() - textAxisMargin - height + 1, xWidth + 2 * textAxisMargin - 2, height + 2 * textAxisMargin - 2 ) );
304  painter->drawRect( QRectF( yCoordOrigin.x() - textAxisMargin + 1, yCoordOrigin.y() - textAxisMargin - height + 1, yWidth + 2 * textAxisMargin - 2, height + 2 * textAxisMargin - 2 ) );
305 
306  painter->setBrush( Qt::NoBrush );
307  painter->setPen( Qt::black );
308 
309  painter->drawText( xCoordOrigin, xCoordinateText );
310  painter->drawText( yCoordOrigin, yCoordinateText );
311  painter->restore();
312  }
313 
314  private:
315 
316  QRectF mRect;
317  QgsProfilePoint mPoint;
318  QgsElevationProfilePlotItem *mPlotItem = nullptr;
319 };
321 
322 
324  : QgsPlotCanvas( parent )
325 {
326  mPlotItem = new QgsElevationProfilePlotItem( this );
327  mCrossHairsItem = new QgsElevationProfileCrossHairsItem( this, mPlotItem );
328  mCrossHairsItem->setZValue( 100 );
329  mCrossHairsItem->hide();
330 
331  // updating the profile plot is deferred on a timer, so that we don't trigger it too often
332  mDeferredRegenerationTimer = new QTimer( this );
333  mDeferredRegenerationTimer->setSingleShot( true );
334  mDeferredRegenerationTimer->stop();
335  connect( mDeferredRegenerationTimer, &QTimer::timeout, this, &QgsElevationProfileCanvas::startDeferredRegeneration );
336 
337  mDeferredRedrawTimer = new QTimer( this );
338  mDeferredRedrawTimer->setSingleShot( true );
339  mDeferredRedrawTimer->stop();
340  connect( mDeferredRedrawTimer, &QTimer::timeout, this, &QgsElevationProfileCanvas::startDeferredRedraw );
341 
342 }
343 
345 {
346  if ( mCurrentJob )
347  {
348  mPlotItem->setRenderer( nullptr );
349  mCurrentJob->deleteLater();
350  mCurrentJob = nullptr;
351  }
352 }
353 
355 {
356  if ( mCurrentJob )
357  {
358  mPlotItem->setRenderer( nullptr );
359  disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsElevationProfileCanvas::generationFinished );
360  mCurrentJob->cancelGeneration();
361  mCurrentJob->deleteLater();
362  mCurrentJob = nullptr;
363  }
364 }
365 
366 void QgsElevationProfileCanvas::panContentsBy( double dx, double dy )
367 {
368  const double dxPercent = dx / mPlotItem->plotArea().width();
369  const double dyPercent = dy / mPlotItem->plotArea().height();
370 
371  // these look backwards, but we are dragging the paper, not the view!
372  const double dxPlot = - dxPercent * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
373  const double dyPlot = dyPercent * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
374 
375  mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
376  mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
377  mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
378  mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
379 
380  refineResults();
381 
382  mPlotItem->updatePlot();
383  emit plotAreaChanged();
384 }
385 
386 void QgsElevationProfileCanvas::centerPlotOn( double x, double y )
387 {
388  if ( !mPlotItem->plotArea().contains( x, y ) )
389  return;
390 
391  const double newCenterX = mPlotItem->xMinimum() + ( x - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
392  const double newCenterY = mPlotItem->yMinimum() + ( mPlotItem->plotArea().bottom() - y ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
393 
394  const double dxPlot = newCenterX - ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5;
395  const double dyPlot = newCenterY - ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5;
396 
397  mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
398  mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
399  mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
400  mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
401 
402  refineResults();
403 
404  mPlotItem->updatePlot();
405  emit plotAreaChanged();
406 }
407 
409 {
410  scalePlot( factor, factor );
411  emit plotAreaChanged();
412 }
413 
414 QgsProfileSnapContext QgsElevationProfileCanvas::snapContext() const
415 {
416  const double toleranceInPixels = QFontMetrics( font() ).horizontalAdvance( ' ' );
417  const double xToleranceInPlotUnits = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) * toleranceInPixels;
418  const double yToleranceInPlotUnits = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * toleranceInPixels;
419 
420  QgsProfileSnapContext context;
421  context.maximumSurfaceDistanceDelta = 2 * xToleranceInPlotUnits;
422  context.maximumSurfaceElevationDelta = 10 * yToleranceInPlotUnits;
423  context.maximumPointDistanceDelta = 4 * xToleranceInPlotUnits;
424  context.maximumPointElevationDelta = 4 * yToleranceInPlotUnits;
425  context.displayRatioElevationVsDistance = ( ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) )
426  / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) );
427 
428  return context;
429 }
430 
431 QgsProfileIdentifyContext QgsElevationProfileCanvas::identifyContext() const
432 {
433  const double toleranceInPixels = QFontMetrics( font() ).horizontalAdvance( ' ' );
434  const double xToleranceInPlotUnits = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) * toleranceInPixels;
435  const double yToleranceInPlotUnits = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * toleranceInPixels;
436 
438  context.maximumSurfaceDistanceDelta = 2 * xToleranceInPlotUnits;
439  context.maximumSurfaceElevationDelta = 10 * yToleranceInPlotUnits;
440  context.maximumPointDistanceDelta = 4 * xToleranceInPlotUnits;
441  context.maximumPointElevationDelta = 4 * yToleranceInPlotUnits;
442  context.displayRatioElevationVsDistance = ( ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) )
443  / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) );
444 
445  context.project = mProject;
446 
447  return context;
448 }
449 
450 void QgsElevationProfileCanvas::setupLayerConnections( QgsMapLayer *layer, bool isDisconnect )
451 {
452  if ( isDisconnect )
453  {
454  disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
455  disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileRenderingPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
456  disconnect( layer, &QgsMapLayer::dataChanged, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
457  }
458  else
459  {
460  connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
461  connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileRenderingPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
462  connect( layer, &QgsMapLayer::dataChanged, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
463  }
464 
465  switch ( layer->type() )
466  {
468  {
469  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
470  if ( isDisconnect )
471  {
472  disconnect( vl, &QgsVectorLayer::featureAdded, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
473  disconnect( vl, &QgsVectorLayer::featureDeleted, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
474  disconnect( vl, &QgsVectorLayer::geometryChanged, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
475  disconnect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
476  }
477  else
478  {
479  connect( vl, &QgsVectorLayer::featureAdded, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
480  connect( vl, &QgsVectorLayer::featureDeleted, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
481  connect( vl, &QgsVectorLayer::geometryChanged, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
482  connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsElevationProfileCanvas::regenerateResultsForLayer );
483  }
484  break;
485  }
493  break;
494  }
495 }
496 
498 {
499  if ( !mCurrentJob || !mSnappingEnabled )
500  return QgsPointXY();
501 
502  const QgsProfilePoint plotPoint = canvasPointToPlotPoint( point );
503 
504  const QgsProfileSnapResult snappedPoint = mCurrentJob->snapPoint( plotPoint, snapContext() );
505  if ( !snappedPoint.isValid() )
506  return QgsPointXY();
507 
508  return plotPointToCanvasPoint( snappedPoint.snappedPoint );
509 }
510 
511 void QgsElevationProfileCanvas::scalePlot( double xFactor, double yFactor )
512 {
513  const double currentWidth = mPlotItem->xMaximum() - mPlotItem->xMinimum();
514  const double currentHeight = mPlotItem->yMaximum() - mPlotItem->yMinimum();
515 
516  const double newWidth = currentWidth / xFactor;
517  const double newHeight = currentHeight / yFactor;
518 
519  const double currentCenterX = ( mPlotItem->xMinimum() + mPlotItem->xMaximum() ) * 0.5;
520  const double currentCenterY = ( mPlotItem->yMinimum() + mPlotItem->yMaximum() ) * 0.5;
521 
522  mPlotItem->setXMinimum( currentCenterX - newWidth * 0.5 );
523  mPlotItem->setXMaximum( currentCenterX + newWidth * 0.5 );
524  mPlotItem->setYMinimum( currentCenterY - newHeight * 0.5 );
525  mPlotItem->setYMaximum( currentCenterY + newHeight * 0.5 );
526 
527  refineResults();
528  mPlotItem->updatePlot();
529  emit plotAreaChanged();
530 }
531 
532 void QgsElevationProfileCanvas::zoomToRect( const QRectF &rect )
533 {
534  const QRectF intersected = rect.intersected( mPlotItem->plotArea() );
535 
536  const double minX = ( intersected.left() - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
537  const double maxX = ( intersected.right() - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
538  const double minY = ( mPlotItem->plotArea().bottom() - intersected.bottom() ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
539  const double maxY = ( mPlotItem->plotArea().bottom() - intersected.top() ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
540 
541  mPlotItem->setXMinimum( minX );
542  mPlotItem->setXMaximum( maxX );
543  mPlotItem->setYMinimum( minY );
544  mPlotItem->setYMaximum( maxY );
545 
546  refineResults();
547  mPlotItem->updatePlot();
548  emit plotAreaChanged();
549 }
550 
551 void QgsElevationProfileCanvas::wheelZoom( QWheelEvent *event )
552 {
553  //get mouse wheel zoom behavior settings
554  QgsSettings settings;
555  double zoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
556 
557  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
558  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() );
559 
560  if ( event->modifiers() & Qt::ControlModifier )
561  {
562  //holding ctrl while wheel zooming results in a finer zoom
563  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
564  }
565 
566  //calculate zoom scale factor
567  bool zoomIn = event->angleDelta().y() > 0;
568  double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
569 
570  QRectF viewportRect = mPlotItem->plotArea();
571  if ( viewportRect.contains( event->pos() ) )
572  {
573  //adjust view center
574  const double oldCenterX = 0.5 * ( mPlotItem->xMaximum() + mPlotItem->xMinimum() );
575  const double oldCenterY = 0.5 * ( mPlotItem->yMaximum() + mPlotItem->yMinimum() );
576 
577  const double eventPosX = ( event->pos().x() - viewportRect.left() ) / viewportRect.width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
578  const double eventPosY = ( viewportRect.bottom() - event->pos().y() ) / viewportRect.height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
579 
580  const double newCenterX = eventPosX + ( ( oldCenterX - eventPosX ) * scaleFactor );
581  const double newCenterY = eventPosY + ( ( oldCenterY - eventPosY ) * scaleFactor );
582 
583  const double dxPlot = newCenterX - ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5;
584  const double dyPlot = newCenterY - ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5;
585 
586  mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
587  mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
588  mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
589  mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
590  }
591 
592  //zoom plot
593  if ( zoomIn )
594  {
595  scalePlot( zoomFactor );
596  }
597  else
598  {
599  scalePlot( 1 / zoomFactor );
600  }
601  emit plotAreaChanged();
602 }
603 
605 {
607  if ( e->isAccepted() )
608  {
609  mCrossHairsItem->hide();
610  return;
611  }
612 
613  QgsProfilePoint plotPoint = canvasPointToPlotPoint( e->pos() );
614  if ( mCurrentJob && mSnappingEnabled && !plotPoint.isEmpty() )
615  {
616  const QgsProfileSnapResult snapResult = mCurrentJob->snapPoint( plotPoint, snapContext() );
617  if ( snapResult.isValid() )
618  plotPoint = snapResult.snappedPoint;
619  }
620 
621  if ( plotPoint.isEmpty() )
622  {
623  mCrossHairsItem->hide();
624  }
625  else
626  {
627  mCrossHairsItem->setPoint( plotPoint );
628  mCrossHairsItem->show();
629  }
630  emit canvasPointHovered( e->pos(), plotPoint );
631 }
632 
634 {
635  return mPlotItem->plotArea();
636 }
637 
639 {
640  if ( !mProject || !profileCurve() )
641  return;
642 
643  if ( mCurrentJob )
644  {
645  mPlotItem->setRenderer( nullptr );
646  disconnect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsElevationProfileCanvas::generationFinished );
647  mCurrentJob->deleteLater();
648  mCurrentJob = nullptr;
649  }
650 
651  QgsProfileRequest request( profileCurve()->clone() );
652  request.setCrs( mCrs );
653  request.setTolerance( mTolerance );
654  request.setTransformContext( mProject->transformContext() );
655  request.setTerrainProvider( mProject->elevationProperties()->terrainProvider() ? mProject->elevationProperties()->terrainProvider()->clone() : nullptr );
656  QgsExpressionContext context;
659  request.setExpressionContext( context );
660 
661  const QList< QgsMapLayer * > layersToGenerate = layers();
662  QList< QgsAbstractProfileSource * > sources;
663  sources.reserve( layersToGenerate .size() );
664  for ( QgsMapLayer *layer : layersToGenerate )
665  {
666  if ( QgsAbstractProfileSource *source = dynamic_cast< QgsAbstractProfileSource * >( layer ) )
667  sources.append( source );
668  }
669 
670  mCurrentJob = new QgsProfilePlotRenderer( sources, request );
671  connect( mCurrentJob, &QgsProfilePlotRenderer::generationFinished, this, &QgsElevationProfileCanvas::generationFinished );
672 
673  QgsProfileGenerationContext generationContext;
674  generationContext.setDpi( QgsApplication::desktop()->logicalDpiX() );
675  generationContext.setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve->length() ) / mPlotItem->plotArea().width() );
676  generationContext.setMapUnitsPerDistancePixel( mProfileCurve->length() / mPlotItem->plotArea().width() );
677  mCurrentJob->setContext( generationContext );
678 
679  mCurrentJob->startGeneration();
680  mPlotItem->setRenderer( mCurrentJob );
681 
682  emit activeJobCountChanged( 1 );
683 }
684 
686 {
687  mZoomFullWhenJobFinished = true;
688 }
689 
690 void QgsElevationProfileCanvas::generationFinished()
691 {
692  if ( !mCurrentJob )
693  return;
694 
695  emit activeJobCountChanged( 0 );
696 
697  if ( mZoomFullWhenJobFinished )
698  {
699  // we only zoom full for the initial generation
700  mZoomFullWhenJobFinished = false;
701  zoomFull();
702  }
703  else
704  {
705  // here we should invalidate cached results only for the layers which have been refined
706 
707  // and if no layers are being refeined, don't invalidate anything
708 
709  mPlotItem->updatePlot();
710  }
711 
712  if ( mForceRegenerationAfterCurrentJobCompletes )
713  {
714  mForceRegenerationAfterCurrentJobCompletes = false;
715  mCurrentJob->invalidateAllRefinableSources();
716  scheduleDeferredRegeneration();
717  }
718 }
719 
720 void QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
721 {
722  // TODO -- handle nicely when existing job is in progress
723  if ( !mCurrentJob || mCurrentJob->isActive() )
724  return;
725 
726  QgsMapLayerElevationProperties *properties = qobject_cast< QgsMapLayerElevationProperties * >( sender() );
727  if ( !properties )
728  return;
729 
730  if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
731  {
732  if ( QgsAbstractProfileSource *source = dynamic_cast< QgsAbstractProfileSource * >( layer ) )
733  {
734  if ( mCurrentJob->invalidateResults( source ) )
735  scheduleDeferredRegeneration();
736  }
737  }
738 }
739 
740 void QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
741 {
742  // TODO -- handle nicely when existing job is in progress
743  if ( !mCurrentJob || mCurrentJob->isActive() )
744  return;
745 
746  QgsMapLayerElevationProperties *properties = qobject_cast< QgsMapLayerElevationProperties * >( sender() );
747  if ( !properties )
748  return;
749 
750  if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
751  {
752  if ( QgsAbstractProfileSource *source = dynamic_cast< QgsAbstractProfileSource * >( layer ) )
753  {
754  mCurrentJob->replaceSource( source );
755  }
756  if ( mPlotItem->redrawResults( layer->id() ) )
757  scheduleDeferredRedraw();
758  }
759 }
760 
761 void QgsElevationProfileCanvas::regenerateResultsForLayer()
762 {
763  if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( sender() ) )
764  {
765  if ( QgsAbstractProfileSource *source = dynamic_cast< QgsAbstractProfileSource * >( layer ) )
766  {
767  if ( mCurrentJob->invalidateResults( source ) )
768  scheduleDeferredRegeneration();
769  }
770  }
771 }
772 
773 void QgsElevationProfileCanvas::scheduleDeferredRegeneration()
774 {
775  if ( !mDeferredRegenerationScheduled )
776  {
777  mDeferredRegenerationTimer->start( 1 );
778  mDeferredRegenerationScheduled = true;
779  }
780 }
781 
782 void QgsElevationProfileCanvas::scheduleDeferredRedraw()
783 {
784  if ( !mDeferredRedrawScheduled )
785  {
786  mDeferredRedrawTimer->start( 1 );
787  mDeferredRedrawScheduled = true;
788  }
789 }
790 
791 void QgsElevationProfileCanvas::startDeferredRegeneration()
792 {
793  if ( mCurrentJob && !mCurrentJob->isActive() )
794  {
795  emit activeJobCountChanged( 1 );
796  mCurrentJob->regenerateInvalidatedResults();
797  }
798  else if ( mCurrentJob )
799  {
800  mForceRegenerationAfterCurrentJobCompletes = true;
801  }
802 
803  mDeferredRegenerationScheduled = false;
804 }
805 
806 void QgsElevationProfileCanvas::startDeferredRedraw()
807 {
808  mPlotItem->update();
809  mDeferredRedrawScheduled = false;
810 }
811 
812 void QgsElevationProfileCanvas::refineResults()
813 {
814  if ( mCurrentJob )
815  {
817  context.setDpi( QgsApplication::desktop()->logicalDpiX() );
818  const double plotDistanceRange = mPlotItem->xMaximum() - mPlotItem->xMinimum();
819  const double plotElevationRange = mPlotItem->yMaximum() - mPlotItem->yMinimum();
820  const double plotDistanceUnitsPerPixel = plotDistanceRange / mPlotItem->plotArea().width();
821 
822  // we round the actual desired map error down to just one significant figure, to avoid tiny differences
823  // as the plot is panned
824  const double targetMaxErrorInMapUnits = MAX_ERROR_PIXELS * plotDistanceUnitsPerPixel;
825  const double factor = std::pow( 10.0, 1 - std::ceil( std::log10( std::fabs( targetMaxErrorInMapUnits ) ) ) );
826  const double roundedErrorInMapUnits = std::floor( targetMaxErrorInMapUnits * factor ) / factor;
827  context.setMaximumErrorMapUnits( roundedErrorInMapUnits );
828 
829  context.setMapUnitsPerDistancePixel( plotDistanceUnitsPerPixel );
830 
831  // for similar reasons we round the minimum distance off to multiples of the maximum error in map units
832  const double distanceMin = std::floor( ( mPlotItem->xMinimum() - plotDistanceRange * 0.05 ) / context.maximumErrorMapUnits() ) * context.maximumErrorMapUnits();
833  context.setDistanceRange( QgsDoubleRange( std::max( 0.0, distanceMin ),
834  mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
835 
836  context.setElevationRange( QgsDoubleRange( mPlotItem->yMinimum() - plotElevationRange * 0.05,
837  mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
838  mCurrentJob->setContext( context );
839  }
840  scheduleDeferredRegeneration();
841 }
842 
844 {
845  if ( !mPlotItem->plotArea().contains( point.x(), point.y() ) )
846  return QgsProfilePoint();
847 
848  return mPlotItem->canvasPointToPlotPoint( point );
849 }
850 
852 {
853  return mPlotItem->plotPointToCanvasPoint( point );
854 }
855 
857 {
858  mProject = project;
859  mPlotItem->mProject = project;
860 }
861 
863 {
864  mCrs = crs;
865 }
866 
868 {
869  mProfileCurve.reset( curve );
870 }
871 
873 {
874  return mProfileCurve.get();
875 }
876 
878 {
879  mTolerance = tolerance;
880 }
881 
883 {
884  return mCrs;
885 }
886 
887 void QgsElevationProfileCanvas::setLayers( const QList<QgsMapLayer *> &layers )
888 {
889  for ( QgsMapLayer *layer : std::as_const( mLayers ) )
890  {
891  setupLayerConnections( layer, true );
892  }
893 
894  // filter list, removing null layers and invalid layers
895  auto filteredList = layers;
896  filteredList.erase( std::remove_if( filteredList.begin(), filteredList.end(),
897  []( QgsMapLayer * layer )
898  {
899  return !layer || !layer->isValid();
900  } ), filteredList.end() );
901 
902  mLayers = _qgis_listRawToQPointer( filteredList );
903  for ( QgsMapLayer *layer : std::as_const( mLayers ) )
904  {
905  setupLayerConnections( layer, false );
906  }
907 }
908 
909 QList<QgsMapLayer *> QgsElevationProfileCanvas::layers() const
910 {
911  return _qgis_listQPointerToRaw( mLayers );
912 }
913 
914 void QgsElevationProfileCanvas::resizeEvent( QResizeEvent *event )
915 {
917  mPlotItem->updateRect();
918  mCrossHairsItem->updateRect();
919 }
920 
921 void QgsElevationProfileCanvas::paintEvent( QPaintEvent *event )
922 {
923  QgsPlotCanvas::paintEvent( event );
924 
925  if ( !mFirstDrawOccurred )
926  {
927  // on first show we need to update the visible rect of the plot. (Not sure why this doesn't work in showEvent, but it doesn't).
928  mFirstDrawOccurred = true;
929  mPlotItem->updateRect();
930  mCrossHairsItem->updateRect();
931  }
932 }
933 
935 {
936  if ( !mPlotItem->plotArea().contains( point.x(), point.y() ) )
937  return QgsPoint();
938 
939  if ( !mProfileCurve )
940  return QgsPoint();
941 
942  const double dx = point.x() - mPlotItem->plotArea().left();
943 
944  const double distanceAlongPlotPercent = dx / mPlotItem->plotArea().width();
945  double distanceAlongCurveLength = distanceAlongPlotPercent * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
946 
947  std::unique_ptr< QgsPoint > mapXyPoint( mProfileCurve->interpolatePoint( distanceAlongCurveLength ) );
948  if ( !mapXyPoint )
949  return QgsPoint();
950 
951  const double mapZ = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * ( mPlotItem->plotArea().bottom() - point.y() ) + mPlotItem->yMinimum();
952 
953  return QgsPoint( mapXyPoint->x(), mapXyPoint->y(), mapZ );
954 }
955 
957 {
958  if ( !mProfileCurve )
959  return QgsPointXY();
960 
961  QgsGeos geos( mProfileCurve.get() );
962  QString error;
963  const double distanceAlongCurve = geos.lineLocatePoint( point, &error );
964 
965  const double distanceAlongCurveOnPlot = distanceAlongCurve - mPlotItem->xMinimum();
966  const double distanceAlongCurvePercent = distanceAlongCurveOnPlot / ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
967  const double distanceAlongPlotRect = distanceAlongCurvePercent * mPlotItem->plotArea().width();
968 
969  const double canvasX = mPlotItem->plotArea().left() + distanceAlongPlotRect;
970 
971  double canvasY = 0;
972  if ( std::isnan( point.z() ) || point.z() < mPlotItem->yMinimum() )
973  {
974  canvasY = mPlotItem->plotArea().top();
975  }
976  else if ( point.z() > mPlotItem->yMaximum() )
977  {
978  canvasY = mPlotItem->plotArea().bottom();
979  }
980  else
981  {
982  const double yPercent = ( point.z() - mPlotItem->yMinimum() ) / ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
983  canvasY = mPlotItem->plotArea().bottom() - mPlotItem->plotArea().height() * yPercent;
984  }
985 
986  return QgsPointXY( canvasX, canvasY );
987 }
988 
990 {
991  if ( !mCurrentJob )
992  return;
993 
994  const QgsDoubleRange zRange = mCurrentJob->zRange();
995 
996  if ( zRange.upper() < zRange.lower() )
997  {
998  // invalid range, e.g. no features found in plot!
999  mPlotItem->setYMinimum( 0 );
1000  mPlotItem->setYMaximum( 10 );
1001  }
1002  else if ( qgsDoubleNear( zRange.lower(), zRange.upper(), 0.0000001 ) )
1003  {
1004  // corner case ... a zero height plot! Just pick an arbitrary +/- 5 height range.
1005  mPlotItem->setYMinimum( zRange.lower() - 5 );
1006  mPlotItem->setYMaximum( zRange.lower() + 5 );
1007  }
1008  else
1009  {
1010  // add 5% margin to height range
1011  const double margin = ( zRange.upper() - zRange.lower() ) * 0.05;
1012  mPlotItem->setYMinimum( zRange.lower() - margin );
1013  mPlotItem->setYMaximum( zRange.upper() + margin );
1014  }
1015 
1016  const double profileLength = profileCurve()->length();
1017  mPlotItem->setXMinimum( 0 );
1018  // just 2% margin to max distance -- any more is overkill and wasted space
1019  mPlotItem->setXMaximum( profileLength * 1.02 );
1020 
1021  refineResults();
1022  mPlotItem->updatePlot();
1023  emit plotAreaChanged();
1024 }
1025 
1026 void QgsElevationProfileCanvas::setVisiblePlotRange( double minimumDistance, double maximumDistance, double minimumElevation, double maximumElevation )
1027 {
1028  mPlotItem->setYMinimum( minimumElevation );
1029  mPlotItem->setYMaximum( maximumElevation );
1030  mPlotItem->setXMinimum( minimumDistance );
1031  mPlotItem->setXMaximum( maximumDistance );
1032  refineResults();
1033  mPlotItem->updatePlot();
1034  emit plotAreaChanged();
1035 }
1036 
1038 {
1039  return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
1040 }
1041 
1043 {
1044  return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
1045 }
1046 
1048 {
1049  return *mPlotItem;
1050 }
1051 
1053 class QgsElevationProfilePlot : public Qgs2DPlot
1054 {
1055  public:
1056 
1057  QgsElevationProfilePlot( QgsProfilePlotRenderer *renderer )
1058  : mRenderer( renderer )
1059  {
1060  }
1061 
1062  void renderContent( QgsRenderContext &rc, const QRectF &plotArea ) override
1063  {
1064  if ( !mRenderer )
1065  return;
1066 
1067  rc.painter()->translate( plotArea.left(), plotArea.top() );
1068  mRenderer->render( rc, plotArea.width(), plotArea.height(), xMinimum(), xMaximum(), yMinimum(), yMaximum() );
1069  rc.painter()->translate( -plotArea.left(), -plotArea.top() );
1070  }
1071 
1072  private:
1073 
1074  QgsProfilePlotRenderer *mRenderer = nullptr;
1075 };
1077 
1078 void QgsElevationProfileCanvas::render( QgsRenderContext &context, double width, double height, const Qgs2DPlot &plotSettings )
1079 {
1080  if ( !mCurrentJob )
1081  return;
1082 
1085 
1086  QgsElevationProfilePlot profilePlot( mCurrentJob );
1087 
1088  // quick and nasty way to transfer settings from another plot class -- in future we probably want to improve this, but let's let the API settle first...
1089  QDomDocument doc;
1090  QDomElement elem = doc.createElement( QStringLiteral( "plot" ) );
1091  QgsReadWriteContext rwContext;
1092  plotSettings.writeXml( elem, doc, rwContext );
1093  profilePlot.readXml( elem, rwContext );
1094 
1095  profilePlot.setSize( QSizeF( width, height ) );
1096  profilePlot.render( context );
1097 }
1098 
1099 QVector<QgsProfileIdentifyResults> QgsElevationProfileCanvas::identify( QPointF point )
1100 {
1101  if ( !mCurrentJob )
1102  return {};
1103 
1104  const QgsProfilePoint plotPoint = canvasPointToPlotPoint( point );
1105 
1106  return mCurrentJob->identify( plotPoint, identifyContext() );
1107 }
1108 
1109 QVector<QgsProfileIdentifyResults> QgsElevationProfileCanvas::identify( const QRectF &rect )
1110 {
1111  if ( !mCurrentJob )
1112  return {};
1113 
1114  const QgsProfilePoint topLeftPlotPoint = canvasPointToPlotPoint( rect.topLeft() );
1115  const QgsProfilePoint bottomRightPlotPoint = canvasPointToPlotPoint( rect.bottomRight() );
1116 
1117  double distance1 = topLeftPlotPoint.distance();
1118  double distance2 = bottomRightPlotPoint.distance();
1119  if ( distance2 < distance1 )
1120  std::swap( distance1, distance2 );
1121 
1122  double elevation1 = topLeftPlotPoint.elevation();
1123  double elevation2 = bottomRightPlotPoint.elevation();
1124  if ( elevation2 < elevation1 )
1125  std::swap( elevation1, elevation2 );
1126 
1127  return mCurrentJob->identify( QgsDoubleRange( distance1, distance2 ), QgsDoubleRange( elevation1, elevation2 ), identifyContext() );
1128 }
1129 
1131 {
1132  setProfileCurve( nullptr );
1133  mPlotItem->setRenderer( nullptr );
1134  mPlotItem->updatePlot();
1135 }
1136 
1138 {
1139  mSnappingEnabled = enabled;
1140 }
QgsProfileIdentifyContext
Encapsulates the context of identifying profile results.
Definition: qgsabstractprofilegenerator.h:125
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
QgsProfileRequest::setTerrainProvider
QgsProfileRequest & setTerrainProvider(QgsAbstractTerrainProvider *provider)
Sets the terrain provider.
Definition: qgsprofilerequest.cpp:130
QgsElevationProfileCanvas::profileCurve
QgsCurve * profileCurve() const
Returns the profile curve.
Definition: qgselevationprofilecanvas.cpp:872
QgsProfilePlotRenderer::zRange
QgsDoubleRange zRange() const
Returns the limits of the retrieved elevation values.
Definition: qgsprofilerenderer.cpp:255
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
Qgs2DPlot::render
void render(QgsRenderContext &context)
Renders the plot.
Definition: qgsplot.cpp:214
QgsPlotCanvas::resizeEvent
void resizeEvent(QResizeEvent *e) override
Definition: qgsplotcanvas.cpp:197
qgsexpressioncontextutils.h
QgsElevationProfileCanvas::centerPlotOn
void centerPlotOn(double x, double y) override
Centers the plot on the plot point corresponding to x, y in canvas units.
Definition: qgselevationprofilecanvas.cpp:386
QgsProfileIdentifyContext::maximumPointElevationDelta
double maximumPointElevationDelta
Maximum allowed snapping delta for the elevation values when identifying a point.
Definition: qgsabstractprofilegenerator.h:139
QgsPlotCanvas::plotAreaChanged
void plotAreaChanged()
Emitted whenever the visible area of the plot is changed.
QgsPointXY::y
double y
Definition: qgspointxy.h:63
Qgs2DPlot::setYMinimum
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
Definition: qgsplot.h:326
QgsElevationProfileCanvas::visibleDistanceRange
QgsDoubleRange visibleDistanceRange() const
Returns the distance range currently visible in the plot.
Definition: qgselevationprofilecanvas.cpp:1037
QgsProfilePlotRenderer::setContext
void setContext(const QgsProfileGenerationContext &context)
Sets the context in which the profile generation will occur.
Definition: qgsprofilerenderer.cpp:140
QgsElevationProfileCanvas::visibleElevationRange
QgsDoubleRange visibleElevationRange() const
Returns the elevation range currently visible in the plot.
Definition: qgselevationprofilecanvas.cpp:1042
QgsProfilePlotRenderer::invalidateResults
bool invalidateResults(QgsAbstractProfileSource *source)
Invalidates the profile results from the source with matching ID.
Definition: qgsprofilerenderer.cpp:195
QgsElevationProfileCanvas::paintEvent
void paintEvent(QPaintEvent *event) override
Definition: qgselevationprofilecanvas.cpp:921
Qgs2DPlot::interiorPlotArea
QRectF interiorPlotArea(QgsRenderContext &context) const
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
Definition: qgsplot.cpp:385
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:625
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsElevationProfileCanvas::layers
QList< QgsMapLayer * > layers() const
Returns the list of layers included in the profile.
Definition: qgselevationprofilecanvas.cpp:909
QgsMapLayerType::MeshLayer
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:40
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsElevationProfileCanvas::canvasPointToPlotPoint
QgsProfilePoint canvasPointToPlotPoint(QPointF point) const
Converts a canvas point to the equivalent plot point.
Definition: qgselevationprofilecanvas.cpp:843
QgsProfileSnapContext::maximumPointElevationDelta
double maximumPointElevationDelta
Maximum allowed snapping delta for the elevation values when snapping to a point.
Definition: qgsprofilesnapping.h:57
QgsMapLayerType::VectorLayer
@ VectorLayer
Vector layer.
QgsElevationProfileCanvas::scalePlot
void scalePlot(double factor) override
Scales the plot by a specified scale factor.
Definition: qgselevationprofilecanvas.cpp:408
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
QgsMapLayerElevationProperties
Base class for storage of map layer elevation properties.
Definition: qgsmaplayerelevationproperties.h:41
QgsAbstractTerrainProvider::clone
virtual QgsAbstractTerrainProvider * clone() const =0
Creates a clone of the provider and returns the new object.
Qgs2DPlot
Base class for 2-dimensional plot/chart/graphs.
Definition: qgsplot.h:234
QgsProfilePlotRenderer::generationFinished
void generationFinished()
Emitted when the profile generation is finished (or canceled).
Qgs2DPlot::setSize
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:380
QgsElevationProfileCanvas::setCrs
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs associated with the canvas' map coordinates.
Definition: qgselevationprofilecanvas.cpp:862
QgsProfileSnapResult
Encapsulates results of snapping a profile point.
Definition: qgsprofilesnapping.h:56
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsMapLayerType::AnnotationLayer
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
QgsElevationProfileCanvas::plot
const Qgs2DPlot & plot() const
Returns a reference to the 2D plot used by the widget.
Definition: qgselevationprofilecanvas.cpp:1047
Qgs2DPlot::yMinimum
double yMinimum() const
Returns the minimum value of the y axis.
Definition: qgsplot.h:319
Qgs2DPlot::writeXml
bool writeXml(QDomElement &element, QDomDocument &document, QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
Definition: qgsplot.cpp:162
geos
Contains geos related utilities and functions.
Definition: qgsgeos.h:41
QgsElevationProfileCanvas::setLayers
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to include in the profile.
Definition: qgselevationprofilecanvas.cpp:887
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
QgsElevationProfileCanvas::snapToPlot
QgsPointXY snapToPlot(QPoint point) override
Snap a canvas point to the plot.
Definition: qgselevationprofilecanvas.cpp:497
QgsPoint::z
double z
Definition: qgspoint.h:71
QgsElevationProfileCanvas::refresh
void refresh() override
Triggers a complete regeneration of the profile, causing the profile extraction to perform in the bac...
Definition: qgselevationprofilecanvas.cpp:638
QgsElevationProfileCanvas::setTolerance
void setTolerance(double tolerance)
Sets the profile tolerance (in crs() units).
Definition: qgselevationprofilecanvas.cpp:877
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsAbstractGeometry::length
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
Definition: qgsabstractgeometry.cpp:166
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsPointXY::isEmpty
bool isEmpty() const SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspointxy.h:249
QgsVectorLayer::featureDeleted
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
QgsProfilePlotRenderer::startGeneration
void startGeneration()
Start the generation job and immediately return.
Definition: qgsprofilerenderer.cpp:60
QgsProfileGenerationContext::setMaximumErrorMapUnits
void setMaximumErrorMapUnits(double error)
Sets the maximum allowed error in the generated result, in profile curve map units.
Definition: qgsabstractprofilegenerator.h:284
QgsProfileGenerationContext::setDpi
void setDpi(double dpi)
Sets the dpi (dots per inch) for the profie, to be used in size conversions.
Definition: qgsabstractprofilegenerator.h:341
qgspoint.h
QgsElevationProfileCanvas::zoomToRect
void zoomToRect(const QRectF &rect) override
Zooms the plot to the specified rect in canvas units.
Definition: qgselevationprofilecanvas.cpp:532
qgsabstractprofilesource.h
Qgs2DPlot::renderContent
virtual void renderContent(QgsRenderContext &context, const QRectF &plotArea)
Renders the plot content.
Definition: qgsplot.cpp:368
QgsProfileSnapContext
Encapsulates the context of snapping a profile point.
Definition: qgsprofilesnapping.h:30
QgsProfileIdentifyContext::maximumPointDistanceDelta
double maximumPointDistanceDelta
Maximum allowed snapping delta for the distance values when identifying a point.
Definition: qgsabstractprofilegenerator.h:136
QgsProfileIdentifyContext::maximumSurfaceElevationDelta
double maximumSurfaceElevationDelta
Maximum allowed snapping delta for the elevation values when identifying a continuous elevation surfa...
Definition: qgsabstractprofilegenerator.h:133
QgsProfilePlotRenderer::snapPoint
QgsProfileSnapResult snapPoint(const QgsProfilePoint &point, const QgsProfileSnapContext &context)
Snap a point to the results.
Definition: qgsprofilerenderer.cpp:325
QgsProfileGenerationContext::setMapUnitsPerDistancePixel
void setMapUnitsPerDistancePixel(double units)
Sets the number of map units per pixel in the distance dimension.
Definition: qgsabstractprofilegenerator.h:298
QgsProfilePoint::isEmpty
bool isEmpty() const SIP_HOLDGIL
Returns true if the point is empty.
Definition: qgsprofilepoint.h:108
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
Qgs2DPlot::yMaximum
double yMaximum() const
Returns the maximum value of the y axis.
Definition: qgsplot.h:347
QgsProfilePoint
Encapsulates a point on a distance-elevation profile.
Definition: qgsprofilepoint.h:30
qgsmaplayerlistutils_p.h
QgsProfileSnapContext::maximumSurfaceDistanceDelta
double maximumSurfaceDistanceDelta
Maximum allowed snapping delta for the distance values when snapping to a continuous elevation surfac...
Definition: qgsprofilesnapping.h:48
QgsProfileGenerationContext
Encapsulates the context in which an elevation profile is to be generated.
Definition: qgsabstractprofilegenerator.h:262
qgsapplication.h
QgsElevationProfileCanvas::wheelZoom
void wheelZoom(QWheelEvent *event) override
Zoom plot from a mouse wheel event.
Definition: qgselevationprofilecanvas.cpp:551
QgsProfileRequest::setTolerance
QgsProfileRequest & setTolerance(double tolerance)
Sets the tolerance of the request (in crs() units).
Definition: qgsprofilerequest.cpp:124
QgsProfilePlotRenderer::isActive
bool isActive() const
Returns true if the generation job is currently running in background.
Definition: qgsprofilerenderer.cpp:135
QgsExpressionContextUtils::projectScope
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Definition: qgsexpressioncontextutils.cpp:291
qgsprofilerenderer.h
QgsProfileRequest::setTransformContext
QgsProfileRequest & setTransformContext(const QgsCoordinateTransformContext &context)
Sets the transform context, for use when transforming coordinates from a source to the request's crs(...
Definition: qgsprofilerequest.cpp:118
QgsRange::lower
T lower() const
Returns the lower bound of the range.
Definition: qgsrange.h:66
qgsplot.h
QgsElevationProfileCanvas::plotPointToCanvasPoint
QgsPointXY plotPointToCanvasPoint(const QgsProfilePoint &point) const
Converts a plot point to the equivalent canvas point.
Definition: qgselevationprofilecanvas.cpp:851
QgsElevationProfileCanvas::clear
void clear()
Clears the current profile.
Definition: qgselevationprofilecanvas.cpp:1130
qgsprofilesnapping.h
QgsElevationProfileCanvas::cancelJobs
void cancelJobs() override
Cancel any rendering job, in a blocking way.
Definition: qgselevationprofilecanvas.cpp:354
QgsProfilePlotRenderer::regenerateInvalidatedResults
void regenerateInvalidatedResults()
Starts a background regeneration of any invalidated results and immediately returns.
Definition: qgsprofilerenderer.cpp:242
QgsElevationProfileCanvas::setProject
void setProject(QgsProject *project)
Sets the project associated with the profile.
Definition: qgselevationprofilecanvas.cpp:856
QgsElevationProfileCanvas
A canvas for elevation profiles.
Definition: qgselevationprofilecanvas.h:45
qgsplotcanvasitem.h
QgsProfilePlotRenderer::identify
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context)
Identify results visible at the specified profile point.
Definition: qgsprofilerenderer.cpp:357
QgsMapLayerType::GroupLayer
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
QgsProfileGenerationContext::setDistanceRange
void setDistanceRange(const QgsDoubleRange &range)
Sets the range of distances to include in the generation.
Definition: qgsabstractprofilegenerator.h:316
QgsElevationProfileCanvas::crs
QgsCoordinateReferenceSystem crs() const override
Returns the coordinate reference system (CRS) for map coordinates used by the canvas.
Definition: qgselevationprofilecanvas.cpp:882
QgsPlotCanvas::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *e) override
Definition: qgsplotcanvas.cpp:216
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsRange::upper
T upper() const
Returns the upper bound of the range.
Definition: qgsrange.h:73
qgsprofilerequest.h
QgsElevationProfileCanvas::render
void render(QgsRenderContext &context, double width, double height, const Qgs2DPlot &plotSettings)
Renders a portion of the profile using the specified render context.
Definition: qgselevationprofilecanvas.cpp:1078
QgsProfileSnapContext::maximumSurfaceElevationDelta
double maximumSurfaceElevationDelta
Maximum allowed snapping delta for the elevation values when snapping to a continuous elevation surfa...
Definition: qgsprofilesnapping.h:51
QgsVectorLayer::attributeValueChanged
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Emitted whenever an attribute value change is done in the edit buffer.
QgsMapLayerType::RasterLayer
@ RasterLayer
Raster layer.
QgsElevationProfileCanvas::plotArea
QRectF plotArea() const
Returns the interior rectangle representing the surface of the plot, in canvas coordinates.
Definition: qgselevationprofilecanvas.cpp:633
QgsGeos
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:103
Qgs2DPlot::xMaximum
double xMaximum() const
Returns the maximum value of the x axis.
Definition: qgsplot.h:333
QgsProfileRequest
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
Definition: qgsprofilerequest.h:37
QgsElevationProfileCanvas::identify
QVector< QgsProfileIdentifyResults > identify(QPointF point)
Identify results visible at the specified plot point.
Definition: qgselevationprofilecanvas.cpp:1099
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:169
QgsProfileSnapResult::snappedPoint
QgsProfilePoint snappedPoint
Snapped point.
Definition: qgsprofilesnapping.h:61
QgsPlotCanvasItem::paint
virtual void paint(QPainter *painter)=0
Paints the item.
QgsProfilePoint::distance
double distance() const SIP_HOLDGIL
Returns the distance of the point.
Definition: qgsprofilepoint.h:88
QgsElevationProfileCanvas::zoomFull
void zoomFull()
Zooms to the full extent of the profile.
Definition: qgselevationprofilecanvas.cpp:989
QgsMapLayerElevationProperties::profileGenerationPropertyChanged
void profileGenerationPropertyChanged()
Emitted when any of the elevation properties which relate solely to generation of elevation profiles ...
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsPlotCanvas
Plot canvas is a class for displaying interactive 2d charts and plots.
Definition: qgsplotcanvas.h:53
QgsProjectElevationProperties::terrainProvider
QgsAbstractTerrainProvider * terrainProvider()
Returns the project's terrain provider.
Definition: qgsprojectelevationproperties.cpp:84
QgsElevationProfileCanvas::invalidateCurrentPlotExtent
void invalidateCurrentPlotExtent()
Invalidates the current plot extent, which means that the visible plot area will be recalculated and ...
Definition: qgselevationprofilecanvas.cpp:685
QgsElevationProfileCanvas::panContentsBy
void panContentsBy(double dx, double dy) override
Pans the plot contents by dx, dy in canvas units.
Definition: qgselevationprofilecanvas.cpp:366
QgsProfileSnapResult::isValid
bool isValid() const
Returns true if the result is a valid point.
Definition: qgsprofilesnapping.h:66
qgsnumericformat.h
QgsPlotCanvas::event
bool event(QEvent *e) override
Definition: qgsplotcanvas.cpp:316
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:494
QgsProfileIdentifyContext::project
QgsProject * project
Associated project.
Definition: qgsabstractprofilegenerator.h:145
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
Qgs2DPlot::setYMaximum
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
Definition: qgsplot.h:354
QgsElevationProfileCanvas::setProfileCurve
void setProfileCurve(QgsCurve *curve)
Sets the profile curve.
Definition: qgselevationprofilecanvas.cpp:867
QgsProfileIdentifyContext::maximumSurfaceDistanceDelta
double maximumSurfaceDistanceDelta
Maximum allowed snapping delta for the distance values when identifying a continuous elevation surfac...
Definition: qgsabstractprofilegenerator.h:130
QgsMapLayer::dataChanged
void dataChanged()
Data of layer changed.
QgsMapLayerElevationProperties::profileRenderingPropertyChanged
void profileRenderingPropertyChanged()
Emitted when any of the elevation properties which relate solely to presentation of elevation results...
Qgs2DPlot::xMinimum
double xMinimum() const
Returns the minimum value of the x axis.
Definition: qgsplot.h:305
QgsDoubleRange
QgsRange which stores a range of double values.
Definition: qgsrange.h:202
QgsElevationProfileCanvas::tolerance
double tolerance() const
Returns the tolerance of the profile (in crs() units).
Definition: qgselevationprofilecanvas.h:164
qgscurve.h
QgsElevationProfileCanvas::setVisiblePlotRange
void setVisiblePlotRange(double minimumDistance, double maximumDistance, double minimumElevation, double maximumElevation)
Sets the visible area of the plot.
Definition: qgselevationprofilecanvas.cpp:1026
qgselevationprofilecanvas.h
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsVectorLayer::geometryChanged
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
QgsElevationProfileCanvas::QgsElevationProfileCanvas
QgsElevationProfileCanvas(QWidget *parent=nullptr)
Constructor for QgsElevationProfileCanvas, with the specified parent widget.
Definition: qgselevationprofilecanvas.cpp:323
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsRenderContext::fromQPainter
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
Definition: qgsrendercontext.cpp:143
QgsProfileRequest::setExpressionContext
QgsProfileRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate expressions.
Definition: qgsprofilerequest.cpp:147
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsElevationProfileCanvas::toCanvasCoordinates
QgsPointXY toCanvasCoordinates(const QgsPoint &point) const override
Converts a point in map coordinates to the associated canvas point.
Definition: qgselevationprofilecanvas.cpp:956
QgsProfileGenerationContext::maximumErrorMapUnits
double maximumErrorMapUnits() const
Returns the maximum allowed error in the generated result, in profile curve map units.
Definition: qgsabstractprofilegenerator.h:274
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
QgsProfileSnapContext::maximumPointDistanceDelta
double maximumPointDistanceDelta
Maximum allowed snapping delta for the distance values when snapping to a point.
Definition: qgsprofilesnapping.h:54
QgsElevationProfileCanvas::~QgsElevationProfileCanvas
~QgsElevationProfileCanvas() override
Definition: qgselevationprofilecanvas.cpp:344
QgsProfilePlotRenderer::invalidateAllRefinableSources
void invalidateAllRefinableSources()
Invalidates previous results from all refinable sources.
Definition: qgsprofilerenderer.cpp:169
QgsProfileGenerationContext::setElevationRange
void setElevationRange(const QgsDoubleRange &range)
Sets the range of elevations to include in the generation.
Definition: qgsabstractprofilegenerator.h:334
Qgs2DPlot::calculateOptimisedIntervals
void calculateOptimisedIntervals(QgsRenderContext &context)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
Definition: qgsplot.cpp:434
QgsProject::elevationProperties
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
Definition: qgsproject.cpp:3521
QgsProfileIdentifyContext::displayRatioElevationVsDistance
double displayRatioElevationVsDistance
Display ratio of elevation vs distance units.
Definition: qgsabstractprofilegenerator.h:142
QgsElevationProfileCanvas::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *e) override
Definition: qgselevationprofilecanvas.cpp:604
qgsterrainprovider.h
QgsProfilePlotRenderer::replaceSource
void replaceSource(QgsAbstractProfileSource *source)
Replaces the existing source with matching ID.
Definition: qgsprofilerenderer.cpp:190
QgsProfileRequest::setCrs
QgsProfileRequest & setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the desired Coordinate Reference System (crs) for the profile.
Definition: qgsprofilerequest.cpp:102
qgsguiutils.h
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:112
QgsProfilePlotRenderer::cancelGeneration
void cancelGeneration()
Stop the generation job - does not return until the job has terminated.
Definition: qgsprofilerenderer.cpp:84
QgsElevationProfileCanvas::canvasPointHovered
void canvasPointHovered(const QgsPointXY &point, const QgsProfilePoint &profilePoint)
Emitted when the mouse hovers over the specified point (in canvas coordinates).
QgsMapLayerType::PointCloudLayer
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
QgsElevationProfileCanvas::setSnappingEnabled
void setSnappingEnabled(bool enabled)
Sets whether snapping of cursor points is enabled.
Definition: qgselevationprofilecanvas.cpp:1137
QgsPlotCanvasItem
An abstract class for items that can be placed on a QgsPlotCanvas.
Definition: qgsplotcanvasitem.h:38
QgsElevationProfileCanvas::toMapCoordinates
QgsPoint toMapCoordinates(const QgsPointXY &point) const override
Converts a point on the canvas to the associated map coordinate.
Definition: qgselevationprofilecanvas.cpp:934
QgsRenderContext::setScaleFactor
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:472
QgsMapLayerType::PluginLayer
@ PluginLayer
Plugin based layer.
QgsMapLayer::elevationProperties
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
Definition: qgsmaplayer.h:1509
QgsNumericFormatContext
A context for numeric formats.
Definition: qgsnumericformat.h:34
QgsAbstractProfileSource
Interface for classes which can generate elevation profiles.
Definition: qgsabstractprofilesource.h:33
qgsabstractprofilegenerator.h
qgsgeos.h
QgsElevationProfileCanvas::resizeEvent
void resizeEvent(QResizeEvent *event) override
Definition: qgselevationprofilecanvas.cpp:914
qgsmaplayerelevationproperties.h
QgsProfilePlotRenderer
Generates and renders elevation profile plots.
Definition: qgsprofilerenderer.h:58
qgsprojectelevationproperties.h
QgsProfilePoint::elevation
double elevation() const SIP_HOLDGIL
Returns the elevation of the point.
Definition: qgsprofilepoint.h:98
QgsProfileSnapContext::displayRatioElevationVsDistance
double displayRatioElevationVsDistance
Display ratio of elevation vs distance units.
Definition: qgsprofilesnapping.h:60
QgsElevationProfileCanvas::activeJobCountChanged
void activeJobCountChanged(int count)
Emitted when the number of active background jobs changes.
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:80
QgsVectorLayer::featureAdded
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.