60 mRect = mCanvas->rect();
63 prepareGeometryChange();
64 setPos( mRect.topLeft() );
67 mCachedImages.clear();
75 mCachedImages.clear();
80 bool redrawResults(
const QString &sourceId )
82 auto it = mCachedImages.find( sourceId );
83 if ( it == mCachedImages.end() )
86 mCachedImages.erase( it );
91 QRectF boundingRect()
const override
98 if ( !mPlotArea.isNull() )
103 if ( !scene()->views().isEmpty() )
104 context.
setScaleFactor( scene()->views().at( 0 )->logicalDpiX() / 25.4 );
113 const QRectF area = plotArea();
114 if ( !area.contains( point.x(), point.y() ) )
127 const QRectF area = plotArea();
136 mPlotArea = plotArea;
141 const double pixelRatio = !scene()->views().empty() ? scene()->views().at( 0 )->devicePixelRatioF() : 1;
143 const QStringList sourceIds = mRenderer->sourceIds();
144 for (
const QString &source : sourceIds )
147 auto it = mCachedImages.constFind( source );
148 if ( it != mCachedImages.constEnd() )
154 plot = mRenderer->renderToImage( plotArea.width() * pixelRatio,
156 plot.setDevicePixelRatio( pixelRatio );
157 mCachedImages.insert( source, plot );
159 rc.
painter()->drawImage( QPointF( plotArea.left(),
160 plotArea.top() ), plot );
164 void paint( QPainter *painter )
override
167 if ( !mImage.isNull() )
169 painter->drawImage( QPointF( 0, 0 ), mImage );
173 const double pixelRatio = !scene()->views().empty() ? scene()->views().at( 0 )->devicePixelRatioF() : 1;
174 mImage = QImage( mRect.width() * pixelRatio, mRect.height() * pixelRatio, QImage::Format_ARGB32_Premultiplied );
175 mImage.setDevicePixelRatio( pixelRatio );
176 mImage.fill( Qt::transparent );
178 QPainter imagePainter( &mImage );
179 imagePainter.setRenderHint( QPainter::Antialiasing,
true );
183 const double mapUnitsPerPixel = (
xMaximum() -
xMinimum() ) / plotArea().width();
193 painter->drawImage( QPointF( 0, 0 ), mImage );
203 QMap< QString, QImage > mCachedImages;
216 , mPlotItem( plotItem )
222 mRect = mCanvas->rect();
224 prepareGeometryChange();
225 setPos( mRect.topLeft() );
235 QRectF boundingRect()
const override
240 void paint( QPainter *painter )
override
242 const QgsPointXY crossHairPlotPoint = mPlotItem->plotPointToCanvasPoint( mPoint );
243 if ( crossHairPlotPoint.
isEmpty() )
247 painter->setBrush( Qt::NoBrush );
249 crossHairPen.setCosmetic(
true );
250 crossHairPen.setWidthF( 1 );
251 crossHairPen.setStyle( Qt::DashLine );
252 crossHairPen.setCapStyle( Qt::FlatCap );
253 crossHairPen.setColor( QColor( 0, 0, 0, 150 ) );
254 painter->setPen( crossHairPen );
255 painter->drawLine( QPointF( mPlotItem->plotArea().left(), crossHairPlotPoint.
y() ), QPointF( mPlotItem->plotArea().right(), crossHairPlotPoint.
y() ) );
256 painter->drawLine( QPointF( crossHairPlotPoint.
x(), mPlotItem->plotArea().top() ), QPointF( crossHairPlotPoint.
x(), mPlotItem->plotArea().bottom() ) );
261 const QString xCoordinateText = mPlotItem->xAxis().numericFormat()->formatDouble( mPoint.distance(), numericContext );
262 const QString yCoordinateText = mPlotItem->yAxis().numericFormat()->formatDouble( mPoint.elevation(), numericContext );
265 const QFontMetrics fm( font );
266 const double height = fm.capHeight();
267 const double xWidth = fm.horizontalAdvance( xCoordinateText );
268 const double yWidth = fm.horizontalAdvance( yCoordinateText );
269 const double textAxisMargin = fm.horizontalAdvance(
' ' );
271 QPointF xCoordOrigin;
272 QPointF yCoordOrigin;
274 if ( mPoint.distance() < ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5 )
276 if ( mPoint.elevation() < ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5 )
279 xCoordOrigin = QPointF( crossHairPlotPoint.
x() + textAxisMargin, mPlotItem->plotArea().top() + height + textAxisMargin );
281 yCoordOrigin = QPointF( mPlotItem->plotArea().right() - yWidth - textAxisMargin, crossHairPlotPoint.
y() - textAxisMargin );
286 xCoordOrigin = QPointF( crossHairPlotPoint.
x() + textAxisMargin, mPlotItem->plotArea().bottom() - textAxisMargin );
288 yCoordOrigin = QPointF( mPlotItem->plotArea().right() - yWidth - textAxisMargin, crossHairPlotPoint.
y() + height + textAxisMargin );
293 if ( mPoint.elevation() < ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5 )
296 xCoordOrigin = QPointF( crossHairPlotPoint.
x() - xWidth - textAxisMargin, mPlotItem->plotArea().top() + height + textAxisMargin );
298 yCoordOrigin = QPointF( mPlotItem->plotArea().left() + textAxisMargin, crossHairPlotPoint.
y() - textAxisMargin );
303 xCoordOrigin = QPointF( crossHairPlotPoint.
x() - xWidth - textAxisMargin, mPlotItem->plotArea().bottom() - textAxisMargin );
305 yCoordOrigin = QPointF( mPlotItem->plotArea().left() + textAxisMargin, crossHairPlotPoint.
y() + height + textAxisMargin );
310 painter->setBrush( QBrush( QColor( 255, 255, 255, 220 ) ) );
311 painter->setPen( Qt::NoPen );
312 painter->drawRect( QRectF( xCoordOrigin.x() - textAxisMargin + 1, xCoordOrigin.y() - textAxisMargin - height + 1, xWidth + 2 * textAxisMargin - 2, height + 2 * textAxisMargin - 2 ) );
313 painter->drawRect( QRectF( yCoordOrigin.x() - textAxisMargin + 1, yCoordOrigin.y() - textAxisMargin - height + 1, yWidth + 2 * textAxisMargin - 2, height + 2 * textAxisMargin - 2 ) );
315 painter->setBrush( Qt::NoBrush );
316 painter->setPen( Qt::black );
318 painter->drawText( xCoordOrigin, xCoordinateText );
319 painter->drawText( yCoordOrigin, yCoordinateText );
327 QgsElevationProfilePlotItem *mPlotItem =
nullptr;
337 mPlotItem =
new QgsElevationProfilePlotItem(
this );
338 mCrossHairsItem =
new QgsElevationProfileCrossHairsItem(
this, mPlotItem );
339 mCrossHairsItem->setZValue( 100 );
340 mCrossHairsItem->hide();
343 mDeferredRegenerationTimer =
new QTimer(
this );
344 mDeferredRegenerationTimer->setSingleShot(
true );
345 mDeferredRegenerationTimer->stop();
346 connect( mDeferredRegenerationTimer, &QTimer::timeout,
this, &QgsElevationProfileCanvas::startDeferredRegeneration );
348 mDeferredRedrawTimer =
new QTimer(
this );
349 mDeferredRedrawTimer->setSingleShot(
true );
350 mDeferredRedrawTimer->stop();
351 connect( mDeferredRedrawTimer, &QTimer::timeout,
this, &QgsElevationProfileCanvas::startDeferredRedraw );
359 mPlotItem->setRenderer(
nullptr );
360 mCurrentJob->deleteLater();
361 mCurrentJob =
nullptr;
369 mPlotItem->setRenderer(
nullptr );
372 mCurrentJob->deleteLater();
373 mCurrentJob =
nullptr;
379 const double dxPercent = dx / mPlotItem->plotArea().width();
380 const double dyPercent = dy / mPlotItem->plotArea().height();
383 const double dxPlot = - dxPercent * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
384 const double dyPlot = dyPercent * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
387 mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
388 mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
389 mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
390 mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
394 mPlotItem->updatePlot();
400 if ( !mPlotItem->plotArea().contains( x, y ) )
403 const double newCenterX = mPlotItem->xMinimum() + ( x - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
404 const double newCenterY = mPlotItem->yMinimum() + ( mPlotItem->plotArea().bottom() - y ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
406 const double dxPlot = newCenterX - ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5;
407 const double dyPlot = newCenterY - ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5;
410 mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
411 mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
412 mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
413 mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
417 mPlotItem->updatePlot();
429 const double toleranceInPixels = QFontMetrics( font() ).horizontalAdvance(
' ' );
430 const double xToleranceInPlotUnits = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) * toleranceInPixels;
431 const double yToleranceInPlotUnits = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * toleranceInPixels;
439 / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) );
446 const double toleranceInPixels = QFontMetrics( font() ).horizontalAdvance(
' ' );
447 const double xToleranceInPlotUnits = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) * toleranceInPixels;
448 const double yToleranceInPlotUnits = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * toleranceInPixels;
456 / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) / ( mPlotItem->plotArea().width() ) );
463void QgsElevationProfileCanvas::setupLayerConnections(
QgsMapLayer *layer,
bool isDisconnect )
478 switch ( layer->
type() )
480 case Qgis::LayerType::Vector:
499 case Qgis::LayerType::Raster:
500 case Qgis::LayerType::Plugin:
501 case Qgis::LayerType::Mesh:
502 case Qgis::LayerType::VectorTile:
503 case Qgis::LayerType::Annotation:
504 case Qgis::LayerType::PointCloud:
505 case Qgis::LayerType::Group:
510void QgsElevationProfileCanvas::adjustRangeForAxisScaleLock(
double &xMinimum,
double &xMaximum,
double &yMinimum,
double &yMaximum )
const
513 const double horizontalScale = ( xMaximum - xMinimum ) / mPlotItem->plotArea().width();
514 const double verticalScale = ( yMaximum - yMinimum ) / mPlotItem->plotArea().height();
515 if ( horizontalScale > verticalScale )
517 const double height = horizontalScale * mPlotItem->plotArea().height();
518 const double deltaHeight = ( yMaximum - yMinimum ) - height;
519 yMinimum += deltaHeight / 2;
520 yMaximum -= deltaHeight / 2;
524 const double width = verticalScale * mPlotItem->plotArea().width();
525 const double deltaWidth = ( xMaximum - xMinimum ) - width;
526 xMinimum += deltaWidth / 2;
527 xMaximum -= deltaWidth / 2;
533 return mLockAxisScales;
538 mLockAxisScales = lock;
539 if ( mLockAxisScales )
541 double xMinimum = mPlotItem->xMinimum();
542 double xMaximum = mPlotItem->xMaximum();
543 double yMinimum = mPlotItem->yMinimum();
544 double yMaximum = mPlotItem->yMaximum();
545 adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
546 mPlotItem->setXMinimum( xMinimum );
547 mPlotItem->setXMaximum( xMaximum );
548 mPlotItem->setYMinimum( yMinimum );
549 mPlotItem->setYMaximum( yMaximum );
552 mPlotItem->updatePlot();
559 if ( !mCurrentJob || !mSnappingEnabled )
573 if ( mLockAxisScales )
576 const double currentWidth = mPlotItem->xMaximum() - mPlotItem->xMinimum();
577 const double currentHeight = mPlotItem->yMaximum() - mPlotItem->yMinimum();
579 const double newWidth = currentWidth / xFactor;
580 const double newHeight = currentHeight / yFactor;
582 const double currentCenterX = ( mPlotItem->xMinimum() + mPlotItem->xMaximum() ) * 0.5;
583 const double currentCenterY = ( mPlotItem->yMinimum() + mPlotItem->yMaximum() ) * 0.5;
585 double xMinimum = currentCenterX - newWidth * 0.5;
586 double xMaximum = currentCenterX + newWidth * 0.5;
587 double yMinimum = currentCenterY - newHeight * 0.5;
588 double yMaximum = currentCenterY + newHeight * 0.5;
589 if ( mLockAxisScales )
591 adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
594 mPlotItem->setXMinimum( xMinimum );
595 mPlotItem->setXMaximum( xMaximum );
596 mPlotItem->setYMinimum( yMinimum );
597 mPlotItem->setYMaximum( yMaximum );
600 mPlotItem->updatePlot();
606 const QRectF intersected = rect.intersected( mPlotItem->plotArea() );
608 double minX = ( intersected.left() - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
609 double maxX = ( intersected.right() - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
610 double minY = ( mPlotItem->plotArea().bottom() - intersected.bottom() ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
611 double maxY = ( mPlotItem->plotArea().bottom() - intersected.top() ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
613 if ( mLockAxisScales )
615 adjustRangeForAxisScaleLock( minX, maxX, minY, maxY );
618 mPlotItem->setXMinimum( minX );
619 mPlotItem->setXMaximum( maxX );
620 mPlotItem->setYMinimum( minY );
621 mPlotItem->setYMaximum( maxY );
624 mPlotItem->updatePlot();
632 double zoomFactor = settings.
value( QStringLiteral(
"qgis/zoom_factor" ), 2 ).toDouble();
633 bool reverseZoom = settings.
value( QStringLiteral(
"qgis/reverse_wheel_zoom" ),
false ).toBool();
634 bool zoomIn = reverseZoom ?
event->angleDelta().y() < 0 :
event->angleDelta().y() > 0;
637 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs(
event->angleDelta().y() );
639 if (
event->modifiers() & Qt::ControlModifier )
642 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
646 double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
648 QRectF viewportRect = mPlotItem->plotArea();
650 if ( viewportRect.contains(
event->position() ) )
653 const double oldCenterX = 0.5 * ( mPlotItem->xMaximum() + mPlotItem->xMinimum() );
654 const double oldCenterY = 0.5 * ( mPlotItem->yMaximum() + mPlotItem->yMinimum() );
656 const double eventPosX = (
event->position().x() - viewportRect.left() ) / viewportRect.width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
657 const double eventPosY = ( viewportRect.bottom() -
event->position().y() ) / viewportRect.height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
659 const double newCenterX = eventPosX + ( ( oldCenterX - eventPosX ) * scaleFactor );
660 const double newCenterY = eventPosY + ( ( oldCenterY - eventPosY ) * scaleFactor );
662 const double dxPlot = newCenterX - ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5;
663 const double dyPlot = newCenterY - ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5;
666 mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
667 mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
668 mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
669 mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
687 if ( e->isAccepted() )
689 mCrossHairsItem->hide();
694 if ( mCurrentJob && mSnappingEnabled && !plotPoint.
isEmpty() )
703 mCrossHairsItem->hide();
707 mCrossHairsItem->setPoint( plotPoint );
708 mCrossHairsItem->show();
715 return mPlotItem->plotArea();
725 mPlotItem->setRenderer(
nullptr );
727 mCurrentJob->deleteLater();
728 mCurrentJob =
nullptr;
741 const QList< QgsMapLayer * > layersToGenerate =
layers();
742 QList< QgsAbstractProfileSource * > sources;
743 sources.reserve( layersToGenerate .size() );
747 sources.append( source );
755 generationContext.
setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve->length() ) / mPlotItem->plotArea().width() );
760 mPlotItem->setRenderer( mCurrentJob );
767 mZoomFullWhenJobFinished =
true;
770void QgsElevationProfileCanvas::generationFinished()
777 if ( mZoomFullWhenJobFinished )
780 mZoomFullWhenJobFinished =
false;
789 mPlotItem->updatePlot();
792 if ( mForceRegenerationAfterCurrentJobCompletes )
794 mForceRegenerationAfterCurrentJobCompletes =
false;
796 scheduleDeferredRegeneration();
800void QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
803 if ( !mCurrentJob || mCurrentJob->
isActive() )
810 if (
QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
815 scheduleDeferredRegeneration();
820void QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
823 if ( !mCurrentJob || mCurrentJob->
isActive() )
830 if (
QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
836 if ( mPlotItem->redrawResults( layer->
id() ) )
837 scheduleDeferredRedraw();
841void QgsElevationProfileCanvas::regenerateResultsForLayer()
843 if (
QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( sender() ) )
848 scheduleDeferredRegeneration();
853void QgsElevationProfileCanvas::scheduleDeferredRegeneration()
855 if ( !mDeferredRegenerationScheduled )
857 mDeferredRegenerationTimer->start( 1 );
858 mDeferredRegenerationScheduled =
true;
862void QgsElevationProfileCanvas::scheduleDeferredRedraw()
864 if ( !mDeferredRedrawScheduled )
866 mDeferredRedrawTimer->start( 1 );
867 mDeferredRedrawScheduled =
true;
871void QgsElevationProfileCanvas::startDeferredRegeneration()
873 if ( mCurrentJob && !mCurrentJob->
isActive() )
878 else if ( mCurrentJob )
880 mForceRegenerationAfterCurrentJobCompletes =
true;
883 mDeferredRegenerationScheduled =
false;
886void QgsElevationProfileCanvas::startDeferredRedraw()
889 mDeferredRedrawScheduled =
false;
892void QgsElevationProfileCanvas::refineResults()
898 const double plotDistanceRange = mPlotItem->xMaximum() - mPlotItem->xMinimum();
899 const double plotElevationRange = mPlotItem->yMaximum() - mPlotItem->yMinimum();
900 const double plotDistanceUnitsPerPixel = plotDistanceRange / mPlotItem->plotArea().width();
904 const double targetMaxErrorInMapUnits = MAX_ERROR_PIXELS * plotDistanceUnitsPerPixel;
905 const double factor = std::pow( 10.0, 1 - std::ceil( std::log10( std::fabs( targetMaxErrorInMapUnits ) ) ) );
906 const double roundedErrorInMapUnits = std::floor( targetMaxErrorInMapUnits * factor ) / factor;
914 mPlotItem->xMaximum() + plotDistanceRange * 0.05 ) );
917 mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
920 scheduleDeferredRegeneration();
925 if ( !mPlotItem->plotArea().contains( point.x(), point.y() ) )
928 return mPlotItem->canvasPointToPlotPoint( point );
933 return mPlotItem->plotPointToCanvasPoint( point );
939 mPlotItem->mProject = project;
949 mProfileCurve.reset( curve );
954 return mProfileCurve.get();
969 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
971 setupLayerConnections( layer,
true );
975 auto filteredList =
layers;
976 filteredList.erase( std::remove_if( filteredList.begin(), filteredList.end(),
979 return !layer || !layer->isValid();
980 } ), filteredList.end() );
982 mLayers = _qgis_listRawToQPointer( filteredList );
983 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
985 setupLayerConnections( layer,
false );
991 return _qgis_listQPointerToRaw( mLayers );
997 mPlotItem->updateRect();
998 mCrossHairsItem->updateRect();
1003 QgsPlotCanvas::paintEvent(
event );
1005 if ( !mFirstDrawOccurred )
1008 mFirstDrawOccurred =
true;
1009 mPlotItem->updateRect();
1010 mCrossHairsItem->updateRect();
1016 if ( !mPlotItem->plotArea().contains( point.
x(), point.
y() ) )
1019 if ( !mProfileCurve )
1022 const double dx = point.
x() - mPlotItem->plotArea().left();
1024 const double distanceAlongPlotPercent = dx / mPlotItem->plotArea().width();
1025 double distanceAlongCurveLength = distanceAlongPlotPercent * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
1027 std::unique_ptr< QgsPoint > mapXyPoint( mProfileCurve->interpolatePoint( distanceAlongCurveLength ) );
1031 const double mapZ = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * ( mPlotItem->plotArea().bottom() - point.
y() ) + mPlotItem->yMinimum();
1033 return QgsPoint( mapXyPoint->x(), mapXyPoint->y(), mapZ );
1038 if ( !mProfileCurve )
1043 const double distanceAlongCurve =
geos.lineLocatePoint( point, &error );
1045 const double distanceAlongCurveOnPlot = distanceAlongCurve - mPlotItem->xMinimum();
1046 const double distanceAlongCurvePercent = distanceAlongCurveOnPlot / ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
1047 const double distanceAlongPlotRect = distanceAlongCurvePercent * mPlotItem->plotArea().width();
1049 const double canvasX = mPlotItem->plotArea().left() + distanceAlongPlotRect;
1052 if ( std::isnan( point.
z() ) || point.
z() < mPlotItem->yMinimum() )
1054 canvasY = mPlotItem->plotArea().top();
1056 else if ( point.
z() > mPlotItem->yMaximum() )
1058 canvasY = mPlotItem->plotArea().bottom();
1062 const double yPercent = ( point.
z() - mPlotItem->yMinimum() ) / ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
1063 canvasY = mPlotItem->plotArea().bottom() - mPlotItem->plotArea().height() * yPercent;
1076 double yMinimum = 0;
1077 double yMaximum = 0;
1088 yMinimum = zRange.
lower() - 5;
1089 yMaximum = zRange.
lower() + 5;
1094 const double margin = ( zRange.
upper() - zRange.
lower() ) * 0.05;
1095 yMinimum = zRange.
lower() - margin;
1096 yMaximum = zRange.
upper() + margin;
1100 double xMinimum = 0;
1102 double xMaximum = profileLength * 1.02;
1104 if ( mLockAxisScales )
1106 adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
1109 mPlotItem->setXMinimum( xMinimum );
1110 mPlotItem->setXMaximum( xMaximum );
1111 mPlotItem->setYMinimum( yMinimum );
1112 mPlotItem->setYMaximum( yMaximum );
1115 mPlotItem->updatePlot();
1121 if ( mLockAxisScales )
1123 adjustRangeForAxisScaleLock( minimumDistance, maximumDistance, minimumElevation, maximumElevation );
1126 mPlotItem->setYMinimum( minimumElevation );
1127 mPlotItem->setYMaximum( maximumElevation );
1128 mPlotItem->setXMinimum( minimumDistance );
1129 mPlotItem->setXMaximum( maximumDistance );
1131 mPlotItem->updatePlot();
1137 return QgsDoubleRange( mPlotItem->xMinimum(), mPlotItem->xMaximum() );
1142 return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
1151class QgsElevationProfilePlot :
public Qgs2DPlot
1156 : mRenderer( renderer )
1165 rc.
painter()->translate( plotArea.left(), plotArea.top() );
1167 rc.
painter()->translate( -plotArea.left(), -plotArea.top() );
1184 QgsElevationProfilePlot profilePlot( mCurrentJob );
1188 QDomElement elem = doc.createElement( QStringLiteral(
"plot" ) );
1190 plotSettings.
writeXml( elem, doc, rwContext );
1191 profilePlot.readXml( elem, rwContext );
1193 profilePlot.setSize( QSizeF( width, height ) );
1194 profilePlot.render( context );
1204 return mCurrentJob->
identify( plotPoint, identifyContext() );
1215 double distance1 = topLeftPlotPoint.
distance();
1216 double distance2 = bottomRightPlotPoint.
distance();
1217 if ( distance2 < distance1 )
1218 std::swap( distance1, distance2 );
1220 double elevation1 = topLeftPlotPoint.
elevation();
1221 double elevation2 = bottomRightPlotPoint.
elevation();
1222 if ( elevation2 < elevation1 )
1223 std::swap( elevation1, elevation2 );
1231 mPlotItem->setRenderer(
nullptr );
1232 mPlotItem->updatePlot();
1237 mSnappingEnabled = enabled;
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.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
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 render(QgsRenderContext &context)
Renders the plot.
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.
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).
Abstract base class for curved geometry type.
QgsRange which stores a range of double values.
A canvas for elevation profiles.
QgsDoubleRange visibleElevationRange() const
Returns the elevation range currently visible in the plot.
QgsCurve * profileCurve() const
Returns the profile curve.
void setTolerance(double tolerance)
Sets the profile tolerance (in crs() units).
void setLockAxisScales(bool lock)
Sets whether the distance and elevation scales are locked to each other.
void setProfileCurve(QgsCurve *curve)
Sets the profile curve.
void zoomToRect(const QRectF &rect) override
Zooms the plot to the specified rect in canvas units.
void activeJobCountChanged(int count)
Emitted when the number of active background jobs changes.
QgsElevationProfileCanvas(QWidget *parent=nullptr)
Constructor for QgsElevationProfileCanvas, with the specified parent widget.
void scalePlot(double factor) override
Scales the plot by a specified scale factor.
void paintEvent(QPaintEvent *event) override
QgsDoubleRange visibleDistanceRange() const
Returns the distance range currently visible in the plot.
void cancelJobs() override
Cancel any rendering job, in a blocking way.
QgsCoordinateReferenceSystem crs() const override
Returns the coordinate reference system (CRS) for map coordinates used by the canvas.
void clear()
Clears the current profile.
QgsProfilePoint canvasPointToPlotPoint(QPointF point) const
Converts a canvas point to the equivalent plot point.
QgsPointXY plotPointToCanvasPoint(const QgsProfilePoint &point) const
Converts a plot point to the equivalent canvas point.
QgsPoint toMapCoordinates(const QgsPointXY &point) const override
Converts a point on the canvas to the associated map coordinate.
bool lockAxisScales() const
Returns true if the distance and elevation scales are locked to each other.
void setVisiblePlotRange(double minimumDistance, double maximumDistance, double minimumElevation, double maximumElevation)
Sets the visible area of the plot.
void canvasPointHovered(const QgsPointXY &point, const QgsProfilePoint &profilePoint)
Emitted when the mouse hovers over the specified point (in canvas coordinates).
void render(QgsRenderContext &context, double width, double height, const Qgs2DPlot &plotSettings)
Renders a portion of the profile using the specified render context.
QgsPointXY snapToPlot(QPoint point) override
Snap a canvas point to the plot.
void setProject(QgsProject *project)
Sets the project associated with the profile.
QList< QgsMapLayer * > layers() const
Returns the list of layers included in the profile.
void resizeEvent(QResizeEvent *event) override
void centerPlotOn(double x, double y) override
Centers the plot on the plot point corresponding to x, y in canvas units.
const Qgs2DPlot & plot() const
Returns a reference to the 2D plot used by the widget.
void wheelZoom(QWheelEvent *event) override
Zoom plot from a mouse wheel event.
void refresh() override
Triggers a complete regeneration of the profile, causing the profile extraction to perform in the bac...
double tolerance() const
Returns the tolerance of the profile (in crs() units).
void mouseMoveEvent(QMouseEvent *e) override
void panContentsBy(double dx, double dy) override
Pans the plot contents by dx, dy in canvas units.
void invalidateCurrentPlotExtent()
Invalidates the current plot extent, which means that the visible plot area will be recalculated and ...
QgsPointXY toCanvasCoordinates(const QgsPoint &point) const override
Converts a point in map coordinates to the associated canvas point.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs associated with the canvas' map coordinates.
~QgsElevationProfileCanvas() override
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to include in the profile.
void zoomFull()
Zooms to the full extent of the profile.
void setSnappingEnabled(bool enabled)
Sets whether snapping of cursor points is enabled.
QVector< QgsProfileIdentifyResults > identify(QPointF point)
Identify results visible at the specified plot point.
QRectF plotArea() const
Returns the interior rectangle representing the surface of the plot, in canvas coordinates.
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.
Does vector analysis using the geos library and handles import, export, exception handling*.
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...
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.
A context for numeric formats.
An abstract class for items that can be placed on a QgsPlotCanvas.
virtual void paint(QPainter *painter)=0
Paints the item.
Plot canvas is a class for displaying interactive 2d charts and plots.
bool event(QEvent *e) override
void plotAreaChanged()
Emitted whenever the visible area of the plot is changed.
void mouseMoveEvent(QMouseEvent *e) override
void resizeEvent(QResizeEvent *e) override
A class to represent a 2D point.
bool isEmpty() const SIP_HOLDGIL
Returns true if the geometry is empty.
Point geometry type, with support for z-dimension and m-values.
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.
Encapsulates the context of identifying profile results.
double maximumPointElevationDelta
Maximum allowed snapping delta for the elevation values when identifying a point.
double maximumPointDistanceDelta
Maximum allowed snapping delta for the distance values when identifying a point.
QgsProject * project
Associated project.
double displayRatioElevationVsDistance
Display ratio of elevation vs distance units.
double maximumSurfaceDistanceDelta
Maximum allowed snapping delta for the distance values when identifying a continuous elevation surfac...
double maximumSurfaceElevationDelta
Maximum allowed snapping delta for the elevation values when identifying a continuous elevation surfa...
Generates and renders elevation profile plots.
QgsProfileSnapResult snapPoint(const QgsProfilePoint &point, const QgsProfileSnapContext &context)
Snap a point to the results.
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.
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context)
Identify results visible at the specified profile point.
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 a point on a distance-elevation profile.
double elevation() const SIP_HOLDGIL
Returns the elevation of the point.
bool isEmpty() const SIP_HOLDGIL
Returns true if the point is empty.
double distance() const SIP_HOLDGIL
Returns the distance of the point.
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 the context of snapping a profile point.
double maximumPointDistanceDelta
Maximum allowed snapping delta for the distance values when snapping to a point.
double maximumSurfaceElevationDelta
Maximum allowed snapping delta for the elevation values when snapping to a continuous elevation surfa...
double maximumPointElevationDelta
Maximum allowed snapping delta for the elevation values when snapping to a point.
double maximumSurfaceDistanceDelta
Maximum allowed snapping delta for the distance values when snapping to a continuous elevation surfac...
double displayRatioElevationVsDistance
Display ratio of elevation vs distance units.
Encapsulates results of snapping a profile point.
bool isValid() const
Returns true if the result is a valid point.
QgsProfilePoint snappedPoint
Snapped point.
QgsAbstractTerrainProvider * terrainProvider()
Returns the project's terrain provider.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
QgsCoordinateTransformContext transformContext
T lower() const
Returns the lower bound of the range.
T upper() const
Returns the upper bound of the range.
The class is used as a container of context for various read/write operations on other objects.
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.
A utility class for dynamic handling of changes to screen properties.
double screenDpi() const
Returns the current screen DPI for the screen that the parent widget appears on.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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.
Contains geos related utilities and functions.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
const QgsCoordinateReferenceSystem & crs