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