62 mRect = mCanvas->rect();
65 prepareGeometryChange();
66 setPos( mRect.topLeft() );
69 mCachedImages.clear();
77 mCachedImages.clear();
82 bool redrawResults(
const QString &sourceId )
84 auto it = mCachedImages.find( sourceId );
85 if ( it == mCachedImages.end() )
88 mCachedImages.erase( it );
93 QRectF boundingRect()
const override
98 QString distanceSuffix()
const
100 switch ( mDistanceUnit )
102 case Qgis::DistanceUnit::Meters:
103 case Qgis::DistanceUnit::Kilometers:
104 case Qgis::DistanceUnit::Feet:
105 case Qgis::DistanceUnit::NauticalMiles:
106 case Qgis::DistanceUnit::Yards:
107 case Qgis::DistanceUnit::Miles:
108 case Qgis::DistanceUnit::Centimeters:
109 case Qgis::DistanceUnit::Millimeters:
113 case Qgis::DistanceUnit::Degrees:
114 return QObject::tr(
"°" );
115 case Qgis::DistanceUnit::Unknown:
123 mDistanceUnit = unit;
130 if ( !mPlotArea.isNull() )
135 if ( !scene()->views().isEmpty() )
136 context.
setScaleFactor( scene()->views().at( 0 )->logicalDpiX() / 25.4 );
145 const QRectF area = plotArea();
146 if ( !area.contains( point.x(), point.y() ) )
149 const double distance = ( point.x() - area.left() ) / area.width() * (
xMaximum() -
xMinimum() ) * mXScaleFactor +
xMinimum() * mXScaleFactor;
156 if ( point.
distance() < xMinimum() * mXScaleFactor || point.
distance() > xMaximum()* mXScaleFactor || point.
elevation() < yMinimum() || point.
elevation() > yMaximum() )
159 const QRectF area = plotArea();
168 mPlotArea = plotArea;
173 const double pixelRatio = !scene()->views().empty() ? scene()->views().at( 0 )->devicePixelRatioF() : 1;
175 const QStringList sourceIds = mRenderer->sourceIds();
176 for (
const QString &source : sourceIds )
179 auto it = mCachedImages.constFind( source );
180 if ( it != mCachedImages.constEnd() )
186 plot = mRenderer->renderToImage( plotArea.width() * pixelRatio,
187 plotArea.height() * pixelRatio, xMinimum() * mXScaleFactor,
xMaximum() * mXScaleFactor,
yMinimum(),
yMaximum(), source, pixelRatio );
188 plot.setDevicePixelRatio( pixelRatio );
189 mCachedImages.insert( source, plot );
191 rc.
painter()->drawImage( QPointF( plotArea.left(),
192 plotArea.top() ), plot );
196 void paint( QPainter *painter )
override
199 if ( !mImage.isNull() )
201 painter->drawImage( QPointF( 0, 0 ), mImage );
205 const double pixelRatio = !scene()->views().empty() ? scene()->views().at( 0 )->devicePixelRatioF() : 1;
206 mImage = QImage( mRect.width() * pixelRatio, mRect.height() * pixelRatio, QImage::Format_ARGB32_Premultiplied );
207 mImage.setDevicePixelRatio( pixelRatio );
208 mImage.fill( Qt::transparent );
210 QPainter imagePainter( &mImage );
211 imagePainter.setRenderHint( QPainter::Antialiasing,
true );
215 const double mapUnitsPerPixel = (
xMaximum() -
xMinimum() ) * mXScaleFactor / plotArea().width();
225 painter->drawImage( QPointF( 0, 0 ), mImage );
230 double mXScaleFactor = 1.0;
238 QMap< QString, QImage > mCachedImages;
251 , mPlotItem( plotItem )
257 mRect = mCanvas->rect();
259 prepareGeometryChange();
260 setPos( mRect.topLeft() );
270 QRectF boundingRect()
const override
275 void paint( QPainter *painter )
override
277 const QgsPointXY crossHairPlotPoint = mPlotItem->plotPointToCanvasPoint( mPoint );
278 if ( crossHairPlotPoint.
isEmpty() )
282 painter->setBrush( Qt::NoBrush );
284 crossHairPen.setCosmetic(
true );
285 crossHairPen.setWidthF( 1 );
286 crossHairPen.setStyle( Qt::DashLine );
287 crossHairPen.setCapStyle( Qt::FlatCap );
288 crossHairPen.setColor( QColor( 0, 0, 0, 150 ) );
289 painter->setPen( crossHairPen );
290 painter->drawLine( QPointF( mPlotItem->plotArea().left(), crossHairPlotPoint.
y() ), QPointF( mPlotItem->plotArea().right(), crossHairPlotPoint.
y() ) );
291 painter->drawLine( QPointF( crossHairPlotPoint.
x(), mPlotItem->plotArea().top() ), QPointF( crossHairPlotPoint.
x(), mPlotItem->plotArea().bottom() ) );
296 const QString xCoordinateText = mPlotItem->xAxis().numericFormat()->formatDouble( mPoint.distance() / mPlotItem->mXScaleFactor, numericContext )
297 + mPlotItem->distanceSuffix();
299 const QString yCoordinateText = mPlotItem->yAxis().numericFormat()->formatDouble( mPoint.elevation(), numericContext );
302 const QFontMetrics fm( font );
303 const double height = fm.capHeight();
304 const double xWidth = fm.horizontalAdvance( xCoordinateText );
305 const double yWidth = fm.horizontalAdvance( yCoordinateText );
306 const double textAxisMargin = fm.horizontalAdvance(
' ' );
308 QPointF xCoordOrigin;
309 QPointF yCoordOrigin;
311 if ( mPoint.distance() < ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5 * mPlotItem->mXScaleFactor )
313 if ( mPoint.elevation() < ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5 )
316 xCoordOrigin = QPointF( crossHairPlotPoint.
x() + textAxisMargin, mPlotItem->plotArea().top() + height + textAxisMargin );
318 yCoordOrigin = QPointF( mPlotItem->plotArea().right() - yWidth - textAxisMargin, crossHairPlotPoint.
y() - textAxisMargin );
323 xCoordOrigin = QPointF( crossHairPlotPoint.
x() + textAxisMargin, mPlotItem->plotArea().bottom() - textAxisMargin );
325 yCoordOrigin = QPointF( mPlotItem->plotArea().right() - yWidth - textAxisMargin, crossHairPlotPoint.
y() + height + textAxisMargin );
330 if ( mPoint.elevation() < ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5 )
333 xCoordOrigin = QPointF( crossHairPlotPoint.
x() - xWidth - textAxisMargin, mPlotItem->plotArea().top() + height + textAxisMargin );
335 yCoordOrigin = QPointF( mPlotItem->plotArea().left() + textAxisMargin, crossHairPlotPoint.
y() - textAxisMargin );
340 xCoordOrigin = QPointF( crossHairPlotPoint.
x() - xWidth - textAxisMargin, mPlotItem->plotArea().bottom() - textAxisMargin );
342 yCoordOrigin = QPointF( mPlotItem->plotArea().left() + textAxisMargin, crossHairPlotPoint.
y() + height + textAxisMargin );
347 painter->setBrush( QBrush( QColor( 255, 255, 255, 220 ) ) );
348 painter->setPen( Qt::NoPen );
349 painter->drawRect( QRectF( xCoordOrigin.x() - textAxisMargin + 1, xCoordOrigin.y() - textAxisMargin - height + 1, xWidth + 2 * textAxisMargin - 2, height + 2 * textAxisMargin - 2 ) );
350 painter->drawRect( QRectF( yCoordOrigin.x() - textAxisMargin + 1, yCoordOrigin.y() - textAxisMargin - height + 1, yWidth + 2 * textAxisMargin - 2, height + 2 * textAxisMargin - 2 ) );
352 painter->setBrush( Qt::NoBrush );
353 painter->setPen( Qt::black );
355 painter->drawText( xCoordOrigin, xCoordinateText );
356 painter->drawText( yCoordOrigin, yCoordinateText );
364 QgsElevationProfilePlotItem *mPlotItem =
nullptr;
374 mPlotItem =
new QgsElevationProfilePlotItem(
this );
375 mCrossHairsItem =
new QgsElevationProfileCrossHairsItem(
this, mPlotItem );
376 mCrossHairsItem->setZValue( 100 );
377 mCrossHairsItem->hide();
380 mDeferredRegenerationTimer =
new QTimer(
this );
381 mDeferredRegenerationTimer->setSingleShot(
true );
382 mDeferredRegenerationTimer->stop();
383 connect( mDeferredRegenerationTimer, &QTimer::timeout,
this, &QgsElevationProfileCanvas::startDeferredRegeneration );
385 mDeferredRedrawTimer =
new QTimer(
this );
386 mDeferredRedrawTimer->setSingleShot(
true );
387 mDeferredRedrawTimer->stop();
388 connect( mDeferredRedrawTimer, &QTimer::timeout,
this, &QgsElevationProfileCanvas::startDeferredRedraw );
396 mPlotItem->setRenderer(
nullptr );
397 mCurrentJob->deleteLater();
398 mCurrentJob =
nullptr;
406 mPlotItem->setRenderer(
nullptr );
409 mCurrentJob->deleteLater();
410 mCurrentJob =
nullptr;
416 const double dxPercent = dx / mPlotItem->plotArea().width();
417 const double dyPercent = dy / mPlotItem->plotArea().height();
420 const double dxPlot = - dxPercent * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
421 const double dyPlot = dyPercent * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
424 mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
425 mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
426 mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
427 mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
431 mPlotItem->updatePlot();
437 if ( !mPlotItem->plotArea().contains( x, y ) )
440 const double newCenterX = mPlotItem->xMinimum() + ( x - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() );
441 const double newCenterY = mPlotItem->yMinimum() + ( mPlotItem->plotArea().bottom() - y ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
443 const double dxPlot = newCenterX - ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5;
444 const double dyPlot = newCenterY - ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5;
447 mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
448 mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
449 mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
450 mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
454 mPlotItem->updatePlot();
466 const double toleranceInPixels = QFontMetrics( font() ).horizontalAdvance(
' ' );
467 const double xToleranceInPlotUnits = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor / ( mPlotItem->plotArea().width() ) * toleranceInPixels;
468 const double yToleranceInPlotUnits = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * toleranceInPixels;
476 / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor / ( mPlotItem->plotArea().width() ) );
483 const double toleranceInPixels = QFontMetrics( font() ).horizontalAdvance(
' ' );
484 const double xToleranceInPlotUnits = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor / ( mPlotItem->plotArea().width() ) * toleranceInPixels;
485 const double yToleranceInPlotUnits = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * toleranceInPixels;
493 / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor / ( mPlotItem->plotArea().width() ) );
500void QgsElevationProfileCanvas::setupLayerConnections(
QgsMapLayer *layer,
bool isDisconnect )
515 switch ( layer->
type() )
517 case Qgis::LayerType::Vector:
536 case Qgis::LayerType::Raster:
537 case Qgis::LayerType::Plugin:
538 case Qgis::LayerType::Mesh:
539 case Qgis::LayerType::VectorTile:
540 case Qgis::LayerType::Annotation:
541 case Qgis::LayerType::PointCloud:
542 case Qgis::LayerType::Group:
547void QgsElevationProfileCanvas::adjustRangeForAxisScaleLock(
double &xMinimum,
double &xMaximum,
double &yMinimum,
double &yMaximum )
const
550 const double horizontalScale = ( xMaximum - xMinimum ) / mPlotItem->plotArea().width();
551 const double verticalScale = ( yMaximum - yMinimum ) / mPlotItem->plotArea().height();
552 if ( horizontalScale > verticalScale )
554 const double height = horizontalScale * mPlotItem->plotArea().height();
555 const double deltaHeight = ( yMaximum - yMinimum ) - height;
556 yMinimum += deltaHeight / 2;
557 yMaximum -= deltaHeight / 2;
561 const double width = verticalScale * mPlotItem->plotArea().width();
562 const double deltaWidth = ( ( xMaximum - xMinimum ) - width );
563 xMinimum += deltaWidth / 2;
564 xMaximum -= deltaWidth / 2;
570 return mDistanceUnit;
575 mDistanceUnit = unit;
576 const double oldMin = mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
577 const double oldMax = mPlotItem->xMaximum() * mPlotItem->mXScaleFactor;
579 mPlotItem->setXAxisUnits( mDistanceUnit );
580 mPlotItem->setXMinimum( oldMin / mPlotItem->mXScaleFactor );
581 mPlotItem->setXMaximum( oldMax / mPlotItem->mXScaleFactor );
582 mPlotItem->updatePlot();
587 return mLockAxisScales;
592 mLockAxisScales = lock;
593 if ( mLockAxisScales )
595 double xMinimum = mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
596 double xMaximum = mPlotItem->xMaximum() * mPlotItem->mXScaleFactor;
597 double yMinimum = mPlotItem->yMinimum();
598 double yMaximum = mPlotItem->yMaximum();
599 adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
600 mPlotItem->setXMinimum( xMinimum / mPlotItem->mXScaleFactor );
601 mPlotItem->setXMaximum( xMaximum / mPlotItem->mXScaleFactor );
602 mPlotItem->setYMinimum( yMinimum );
603 mPlotItem->setYMaximum( yMaximum );
606 mPlotItem->updatePlot();
613 if ( !mCurrentJob || !mSnappingEnabled )
627 if ( mLockAxisScales )
630 const double currentWidth = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor;
631 const double currentHeight = mPlotItem->yMaximum() - mPlotItem->yMinimum();
633 const double newWidth = currentWidth / xFactor;
634 const double newHeight = currentHeight / yFactor;
636 const double currentCenterX = ( mPlotItem->xMinimum() + mPlotItem->xMaximum() ) * 0.5 * mPlotItem->mXScaleFactor;
637 const double currentCenterY = ( mPlotItem->yMinimum() + mPlotItem->yMaximum() ) * 0.5;
639 double xMinimum = currentCenterX - newWidth * 0.5;
640 double xMaximum = currentCenterX + newWidth * 0.5;
641 double yMinimum = currentCenterY - newHeight * 0.5;
642 double yMaximum = currentCenterY + newHeight * 0.5;
643 if ( mLockAxisScales )
645 adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
648 mPlotItem->setXMinimum( xMinimum / mPlotItem->mXScaleFactor );
649 mPlotItem->setXMaximum( xMaximum / mPlotItem->mXScaleFactor );
650 mPlotItem->setYMinimum( yMinimum );
651 mPlotItem->setYMaximum( yMaximum );
654 mPlotItem->updatePlot();
660 const QRectF intersected = rect.intersected( mPlotItem->plotArea() );
662 double minX = ( intersected.left() - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor + mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
663 double maxX = ( intersected.right() - mPlotItem->plotArea().left() ) / mPlotItem->plotArea().width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor + mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
664 double minY = ( mPlotItem->plotArea().bottom() - intersected.bottom() ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
665 double maxY = ( mPlotItem->plotArea().bottom() - intersected.top() ) / mPlotItem->plotArea().height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
667 if ( mLockAxisScales )
669 adjustRangeForAxisScaleLock( minX, maxX, minY, maxY );
672 mPlotItem->setXMinimum( minX / mPlotItem->mXScaleFactor );
673 mPlotItem->setXMaximum( maxX / mPlotItem->mXScaleFactor );
674 mPlotItem->setYMinimum( minY );
675 mPlotItem->setYMaximum( maxY );
678 mPlotItem->updatePlot();
686 double zoomFactor = settings.
value( QStringLiteral(
"qgis/zoom_factor" ), 2 ).toDouble();
687 bool reverseZoom = settings.
value( QStringLiteral(
"qgis/reverse_wheel_zoom" ),
false ).toBool();
688 bool zoomIn = reverseZoom ?
event->angleDelta().y() < 0 :
event->angleDelta().y() > 0;
691 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs(
event->angleDelta().y() );
693 if (
event->modifiers() & Qt::ControlModifier )
696 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
700 double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
702 QRectF viewportRect = mPlotItem->plotArea();
704 if ( viewportRect.contains(
event->position() ) )
707 const double oldCenterX = 0.5 * ( mPlotItem->xMaximum() + mPlotItem->xMinimum() );
708 const double oldCenterY = 0.5 * ( mPlotItem->yMaximum() + mPlotItem->yMinimum() );
710 const double eventPosX = (
event->position().x() - viewportRect.left() ) / viewportRect.width() * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) + mPlotItem->xMinimum();
711 const double eventPosY = ( viewportRect.bottom() -
event->position().y() ) / viewportRect.height() * ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) + mPlotItem->yMinimum();
713 const double newCenterX = eventPosX + ( ( oldCenterX - eventPosX ) * scaleFactor );
714 const double newCenterY = eventPosY + ( ( oldCenterY - eventPosY ) * scaleFactor );
716 const double dxPlot = newCenterX - ( mPlotItem->xMaximum() + mPlotItem->xMinimum() ) * 0.5;
717 const double dyPlot = newCenterY - ( mPlotItem->yMaximum() + mPlotItem->yMinimum() ) * 0.5;
720 mPlotItem->setXMinimum( mPlotItem->xMinimum() + dxPlot );
721 mPlotItem->setXMaximum( mPlotItem->xMaximum() + dxPlot );
722 mPlotItem->setYMinimum( mPlotItem->yMinimum() + dyPlot );
723 mPlotItem->setYMaximum( mPlotItem->yMaximum() + dyPlot );
741 if ( e->isAccepted() )
743 mCrossHairsItem->hide();
748 if ( mCurrentJob && mSnappingEnabled && !plotPoint.
isEmpty() )
757 mCrossHairsItem->hide();
761 mCrossHairsItem->setPoint( plotPoint );
762 mCrossHairsItem->show();
769 return mPlotItem->plotArea();
779 mPlotItem->setRenderer(
nullptr );
781 mCurrentJob->deleteLater();
782 mCurrentJob =
nullptr;
795 const QList< QgsMapLayer * > layersToGenerate =
layers();
796 QList< QgsAbstractProfileSource * > sources;
797 sources.reserve( layersToGenerate .size() );
801 sources.append( source );
809 generationContext.
setMaximumErrorMapUnits( MAX_ERROR_PIXELS * ( mProfileCurve->length() ) / mPlotItem->plotArea().width() );
814 mPlotItem->setRenderer( mCurrentJob );
821 mZoomFullWhenJobFinished =
true;
824void QgsElevationProfileCanvas::generationFinished()
831 if ( mZoomFullWhenJobFinished )
834 mZoomFullWhenJobFinished =
false;
843 mPlotItem->updatePlot();
846 if ( mForceRegenerationAfterCurrentJobCompletes )
848 mForceRegenerationAfterCurrentJobCompletes =
false;
850 scheduleDeferredRegeneration();
854void QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
857 if ( !mCurrentJob || mCurrentJob->
isActive() )
864 if (
QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
869 scheduleDeferredRegeneration();
874void QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
877 if ( !mCurrentJob || mCurrentJob->
isActive() )
884 if (
QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
890 if ( mPlotItem->redrawResults( layer->
id() ) )
891 scheduleDeferredRedraw();
895void QgsElevationProfileCanvas::regenerateResultsForLayer()
897 if (
QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( sender() ) )
902 scheduleDeferredRegeneration();
907void QgsElevationProfileCanvas::scheduleDeferredRegeneration()
909 if ( !mDeferredRegenerationScheduled )
911 mDeferredRegenerationTimer->start( 1 );
912 mDeferredRegenerationScheduled =
true;
916void QgsElevationProfileCanvas::scheduleDeferredRedraw()
918 if ( !mDeferredRedrawScheduled )
920 mDeferredRedrawTimer->start( 1 );
921 mDeferredRedrawScheduled =
true;
925void QgsElevationProfileCanvas::startDeferredRegeneration()
927 if ( mCurrentJob && !mCurrentJob->
isActive() )
932 else if ( mCurrentJob )
934 mForceRegenerationAfterCurrentJobCompletes =
true;
937 mDeferredRegenerationScheduled =
false;
940void QgsElevationProfileCanvas::startDeferredRedraw()
943 mDeferredRedrawScheduled =
false;
946void QgsElevationProfileCanvas::refineResults()
952 const double plotDistanceRange = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor;
953 const double plotElevationRange = mPlotItem->yMaximum() - mPlotItem->yMinimum();
954 const double plotDistanceUnitsPerPixel = plotDistanceRange / mPlotItem->plotArea().width();
958 const double targetMaxErrorInMapUnits = MAX_ERROR_PIXELS * plotDistanceUnitsPerPixel;
959 const double factor = std::pow( 10.0, 1 - std::ceil( std::log10( std::fabs( targetMaxErrorInMapUnits ) ) ) );
960 const double roundedErrorInMapUnits = std::floor( targetMaxErrorInMapUnits * factor ) / factor;
968 mPlotItem->xMaximum() * mPlotItem->mXScaleFactor + plotDistanceRange * 0.05 ) );
971 mPlotItem->yMaximum() + plotElevationRange * 0.05 ) );
974 scheduleDeferredRegeneration();
979 if ( !mPlotItem->plotArea().contains( point.x(), point.y() ) )
982 return mPlotItem->canvasPointToPlotPoint( point );
987 return mPlotItem->plotPointToCanvasPoint( point );
993 mPlotItem->mProject = project;
1003 mProfileCurve.reset( curve );
1008 return mProfileCurve.get();
1023 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
1025 setupLayerConnections( layer,
true );
1029 auto filteredList =
layers;
1030 filteredList.erase( std::remove_if( filteredList.begin(), filteredList.end(),
1033 return !layer || !layer->isValid();
1034 } ), filteredList.end() );
1036 mLayers = _qgis_listRawToQPointer( filteredList );
1037 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
1039 setupLayerConnections( layer,
false );
1045 return _qgis_listQPointerToRaw( mLayers );
1051 mPlotItem->updateRect();
1052 mCrossHairsItem->updateRect();
1057 QgsPlotCanvas::paintEvent(
event );
1059 if ( !mFirstDrawOccurred )
1062 mFirstDrawOccurred =
true;
1063 mPlotItem->updateRect();
1064 mCrossHairsItem->updateRect();
1070 if ( !mPlotItem->plotArea().contains( point.
x(), point.
y() ) )
1073 if ( !mProfileCurve )
1076 const double dx = point.
x() - mPlotItem->plotArea().left();
1078 const double distanceAlongPlotPercent = dx / mPlotItem->plotArea().width();
1079 double distanceAlongCurveLength = distanceAlongPlotPercent * ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor + mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
1081 std::unique_ptr< QgsPoint > mapXyPoint( mProfileCurve->interpolatePoint( distanceAlongCurveLength ) );
1085 const double mapZ = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / ( mPlotItem->plotArea().height() ) * ( mPlotItem->plotArea().bottom() - point.
y() ) + mPlotItem->yMinimum();
1087 return QgsPoint( mapXyPoint->x(), mapXyPoint->y(), mapZ );
1092 if ( !mProfileCurve )
1097 const double distanceAlongCurve =
geos.lineLocatePoint( point, &error );
1099 const double distanceAlongCurveOnPlot = distanceAlongCurve - mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
1100 const double distanceAlongCurvePercent = distanceAlongCurveOnPlot / ( ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor );
1101 const double distanceAlongPlotRect = distanceAlongCurvePercent * mPlotItem->plotArea().width();
1103 const double canvasX = mPlotItem->plotArea().left() + distanceAlongPlotRect;
1106 if ( std::isnan( point.
z() ) || point.
z() < mPlotItem->yMinimum() )
1108 canvasY = mPlotItem->plotArea().top();
1110 else if ( point.
z() > mPlotItem->yMaximum() )
1112 canvasY = mPlotItem->plotArea().bottom();
1116 const double yPercent = ( point.
z() - mPlotItem->yMinimum() ) / ( mPlotItem->yMaximum() - mPlotItem->yMinimum() );
1117 canvasY = mPlotItem->plotArea().bottom() - mPlotItem->plotArea().height() * yPercent;
1130 double yMinimum = 0;
1131 double yMaximum = 0;
1142 yMinimum = zRange.
lower() - 5;
1143 yMaximum = zRange.
lower() + 5;
1148 const double margin = ( zRange.
upper() - zRange.
lower() ) * 0.05;
1149 yMinimum = zRange.
lower() - margin;
1150 yMaximum = zRange.
upper() + margin;
1154 double xMinimum = 0;
1156 double xMaximum = profileLength * 1.02;
1158 if ( mLockAxisScales )
1160 adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
1163 mPlotItem->setXMinimum( xMinimum / mPlotItem->mXScaleFactor );
1164 mPlotItem->setXMaximum( xMaximum / mPlotItem->mXScaleFactor );
1165 mPlotItem->setYMinimum( yMinimum );
1166 mPlotItem->setYMaximum( yMaximum );
1169 mPlotItem->updatePlot();
1175 if ( mLockAxisScales )
1177 adjustRangeForAxisScaleLock( minimumDistance, maximumDistance, minimumElevation, maximumElevation );
1180 mPlotItem->setYMinimum( minimumElevation );
1181 mPlotItem->setYMaximum( maximumElevation );
1182 mPlotItem->setXMinimum( minimumDistance / mPlotItem->mXScaleFactor );
1183 mPlotItem->setXMaximum( maximumDistance / mPlotItem->mXScaleFactor );
1185 mPlotItem->updatePlot();
1191 return QgsDoubleRange( mPlotItem->xMinimum() * mPlotItem->mXScaleFactor, mPlotItem->xMaximum() * mPlotItem->mXScaleFactor );
1196 return QgsDoubleRange( mPlotItem->yMinimum(), mPlotItem->yMaximum() );
1205class QgsElevationProfilePlot :
public Qgs2DPlot
1210 : mRenderer( renderer )
1219 rc.
painter()->translate( plotArea.left(), plotArea.top() );
1220 mRenderer->render( rc, plotArea.width(), plotArea.height(), xMinimum() * mXScale,
xMaximum() * mXScale,
yMinimum(),
yMaximum() );
1221 rc.
painter()->translate( -plotArea.left(), -plotArea.top() );
1240 QgsElevationProfilePlot profilePlot( mCurrentJob );
1244 QDomElement elem = doc.createElement( QStringLiteral(
"plot" ) );
1246 plotSettings.
writeXml( elem, doc, rwContext );
1247 profilePlot.readXml( elem, rwContext );
1249 profilePlot.mXScale = mPlotItem->mXScaleFactor;
1250 profilePlot.xAxis().setLabelSuffix( mPlotItem->xAxis().labelSuffix() );
1251 profilePlot.xAxis().setLabelSuffixPlacement( mPlotItem->xAxis().labelSuffixPlacement() );
1253 profilePlot.setSize( QSizeF( width, height ) );
1254 profilePlot.render( context );
1264 return mCurrentJob->
identify( plotPoint, identifyContext() );
1275 double distance1 = topLeftPlotPoint.
distance();
1276 double distance2 = bottomRightPlotPoint.
distance();
1277 if ( distance2 < distance1 )
1278 std::swap( distance1, distance2 );
1280 double elevation1 = topLeftPlotPoint.
elevation();
1281 double elevation2 = bottomRightPlotPoint.
elevation();
1282 if ( elevation2 < elevation1 )
1283 std::swap( elevation1, elevation2 );
1291 mPlotItem->setRenderer(
nullptr );
1292 mPlotItem->updatePlot();
1297 mSnappingEnabled = enabled;
@ FirstAndLastLabels
Place suffix after the first and last label values only.
DistanceUnit
Units of distance.
@ Inches
Inches (since QGIS 3.32)
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.
QgsPlotAxis & xAxis()
Returns a reference to the plot's x axis.
void setSize(QSizeF size)
Sets the overall size of the plot (including titles and over components which sit outside the plot ar...
double xMaximum() const
Returns the maximum value of the x axis.
void 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).
Q_GADGET Qgis::DistanceUnit mapUnits
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.
void setDistanceUnit(Qgis::DistanceUnit unit)
Sets the distance unit used by the canvas.
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...
Qgis::DistanceUnit distanceUnit() const
Returns the distance unit used by the canvas.
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.
void setLabelSuffixPlacement(Qgis::PlotAxisSuffixPlacement placement)
Sets the placement for the axis label suffixes.
void setLabelSuffix(const QString &suffix)
Sets the axis label suffix.
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.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE QString toAbbreviatedString(Qgis::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
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.
#define BUILTIN_UNREACHABLE
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
const QgsCoordinateReferenceSystem & crs