QGIS API Documentation  3.9.0-Master (224899f119)
qgsmapcanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 qgsmapcanvas.cpp - description
3 ------------------ -
4 begin : Sun Jun 30 2002
5 copyright : (C) 2002 by Gary E.Sherman
6 email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <cmath>
19 
20 #include <QtGlobal>
21 #include <QApplication>
22 #include <QCursor>
23 #include <QDir>
24 #include <QFile>
25 #include <QGraphicsItem>
26 #include <QGraphicsScene>
27 #include <QGraphicsView>
28 #include <QKeyEvent>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QPixmap>
32 #include <QRect>
33 #include <QTextStream>
34 #include <QResizeEvent>
35 #include <QScreen>
36 #include <QString>
37 #include <QStringList>
38 #include <QWheelEvent>
39 #include <QWindow>
40 
41 #include "qgis.h"
42 #include "qgssettings.h"
44 #include "qgsapplication.h"
45 #include "qgsexception.h"
47 #include "qgsfeatureiterator.h"
48 #include "qgslogger.h"
49 #include "qgsmapcanvas.h"
50 #include "qgsmapcanvasmap.h"
52 #include "qgsmaplayer.h"
53 #include "qgsmapmouseevent.h"
54 #include "qgsmaptoolpan.h"
55 #include "qgsmaptoolzoom.h"
56 #include "qgsmaptopixel.h"
57 #include "qgsmapoverviewcanvas.h"
58 #include "qgsmaprenderercache.h"
62 #include "qgsmapsettingsutils.h"
63 #include "qgsmessagelog.h"
64 #include "qgsmessageviewer.h"
65 #include "qgspallabeling.h"
66 #include "qgsproject.h"
67 #include "qgsrubberband.h"
68 #include "qgsvectorlayer.h"
69 #include "qgsmapthemecollection.h"
71 #include "qgssvgcache.h"
72 #include "qgsimagecache.h"
74 
80 //TODO QGIS 4.0 - remove
82 {
83  public:
84 
88  CanvasProperties() = default;
89 
91  bool mouseButtonDown{ false };
92 
94  QPoint mouseLastXY;
95 
98 
100  bool panSelectorDown{ false };
101 };
102 
103 
104 
105 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
106  : QGraphicsView( parent )
108  , mExpressionContextScope( tr( "Map Canvas" ) )
109 {
110  mScene = new QGraphicsScene();
111  setScene( mScene );
112  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
113  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
114  setMouseTracking( true );
115  setFocusPolicy( Qt::StrongFocus );
116 
117  mResizeTimer = new QTimer( this );
118  mResizeTimer->setSingleShot( true );
119  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
120 
121  mRefreshTimer = new QTimer( this );
122  mRefreshTimer->setSingleShot( true );
123  connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
124 
125  // create map canvas item which will show the map
126  mMap = new QgsMapCanvasMap( this );
127 
128  // project handling
130  this, &QgsMapCanvas::readProject );
133 
134  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
135  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
136 
140  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
142  this, [ = ]
143  {
144  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
145  refresh();
146  } );
147  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
149  this, [ = ]
150  {
151  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
153  refresh();
154  } );
155 
156  // refresh canvas when a remote svg/image has finished downloading
159  // refresh canvas when project color scheme is changed -- if layers use project colors, they need to be redrawn
161 
162  //segmentation parameters
163  QgsSettings settings;
164  double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
165  QgsAbstractGeometry::SegmentationToleranceType toleranceType = settings.enumValue( QStringLiteral( "qgis/segmentationToleranceType" ), QgsAbstractGeometry::MaximumAngle );
166  mSettings.setSegmentationTolerance( segmentationTolerance );
167  mSettings.setSegmentationToleranceType( toleranceType );
168 
169  mWheelZoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
170 
171  QSize s = viewport()->size();
172  mSettings.setOutputSize( s );
173  mSettings.setDevicePixelRatio( devicePixelRatio() );
174  setSceneRect( 0, 0, s.width(), s.height() );
175  mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
176 
177  moveCanvasContents( true );
178 
179  // keep device pixel ratio up to date on screen or resolution change
180  if ( window()->windowHandle() )
181  {
182  connect( window()->windowHandle(), &QWindow::screenChanged, this, [ = ]( QScreen * ) {mSettings.setDevicePixelRatio( devicePixelRatio() );} );
183  connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, [ = ]( qreal ) {mSettings.setDevicePixelRatio( devicePixelRatio() );} );
184  }
185 
186  connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsMapCanvas::mapUpdateTimeout );
187  mMapUpdateTimer.setInterval( 250 );
188 
189 #ifdef Q_OS_WIN
190  // Enable touch event on Windows.
191  // Qt on Windows needs to be told it can take touch events or else it ignores them.
192  grabGesture( Qt::PinchGesture );
193  viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
194 #endif
195 
196  mPreviewEffect = new QgsPreviewEffect( this );
197  viewport()->setGraphicsEffect( mPreviewEffect );
198 
199  mZoomCursor = QgsApplication::getThemeCursor( QgsApplication::Cursor::ZoomIn );
200 
201  connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
202 
204 
205  setInteractive( false );
206 
207  // make sure we have the same default in QgsMapSettings and the scene's background brush
208  // (by default map settings has white bg color, scene background brush is black)
209  setCanvasColor( mSettings.backgroundColor() );
210 
211  refresh();
212 
213 } // QgsMapCanvas ctor
214 
215 
217 {
218  if ( mMapTool )
219  {
220  mMapTool->deactivate();
221  mMapTool = nullptr;
222  }
223  mLastNonZoomMapTool = nullptr;
224 
225  // rendering job may still end up writing into canvas map item
226  // so kill it before deleting canvas items
227  if ( mJob )
228  {
229  whileBlocking( mJob )->cancel();
230  delete mJob;
231  }
232 
233  QList< QgsMapRendererQImageJob * >::const_iterator previewJob = mPreviewJobs.constBegin();
234  for ( ; previewJob != mPreviewJobs.constEnd(); ++previewJob )
235  {
236  if ( *previewJob )
237  {
238  whileBlocking( *previewJob )->cancel();
239  delete *previewJob;
240  }
241  }
242 
243  // delete canvas items prior to deleting the canvas
244  // because they might try to update canvas when it's
245  // already being destructed, ends with segfault
246  qDeleteAll( mScene->items() );
247 
248  mScene->deleteLater(); // crashes in python tests on windows
249 
250  delete mCache;
251  delete mLabelingResults;
252 }
253 
255 {
256  // do not go higher or lower than min max magnification ratio
257  double magnifierMin = QgsGuiUtils::CANVAS_MAGNIFICATION_MIN;
258  double magnifierMax = QgsGuiUtils::CANVAS_MAGNIFICATION_MAX;
259  factor = qBound( magnifierMin, factor, magnifierMax );
260 
261  // the magnifier widget is in integer percent
262  if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
263  {
264  mSettings.setMagnificationFactor( factor );
265  refresh();
266  emit magnificationChanged( factor );
267  }
268 }
269 
271 {
272  return mSettings.magnificationFactor();
273 }
274 
276 {
277  mSettings.setFlag( QgsMapSettings::Antialiasing, flag );
278 } // anti aliasing
279 
281 {
282  mSettings.setFlag( QgsMapSettings::RenderMapTile, flag );
283 }
284 
286 {
287  QList<QgsMapLayer *> layers = mapSettings().layers();
288  if ( index >= 0 && index < layers.size() )
289  return layers[index];
290  else
291  return nullptr;
292 }
293 
295 {
296  if ( mCurrentLayer == layer )
297  return;
298 
299  mCurrentLayer = layer;
300  emit currentLayerChanged( layer );
301 }
302 
303 double QgsMapCanvas::scale() const
304 {
305  return mapSettings().scale();
306 }
307 
309 {
310  return nullptr != mJob;
311 } // isDrawing
312 
313 // return the current coordinate transform based on the extents and
314 // device size
316 {
317  return &mapSettings().mapToPixel();
318 }
319 
320 void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
321 {
322  // following a theme => request denied!
323  if ( !mTheme.isEmpty() )
324  return;
325 
326  setLayersPrivate( layers );
327 }
328 
329 void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
330 {
331  QList<QgsMapLayer *> oldLayers = mSettings.layers();
332 
333  // update only if needed
334  if ( layers == oldLayers )
335  return;
336 
337  const auto constOldLayers = oldLayers;
338  for ( QgsMapLayer *layer : constOldLayers )
339  {
340  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
341  disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
342  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
343  {
345  }
346  }
347 
348  mSettings.setLayers( layers );
349 
350  const auto constLayers = layers;
351  for ( QgsMapLayer *layer : constLayers )
352  {
353  if ( !layer )
354  continue;
355  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
356  connect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
357  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
358  {
360  }
361  }
362 
363  QgsDebugMsg( QStringLiteral( "Layers have changed, refreshing" ) );
364  emit layersChanged();
365 
366  updateAutoRefreshTimer();
367  refresh();
368 }
369 
370 
372 {
373  return mSettings;
374 }
375 
377 {
378  if ( mSettings.destinationCrs() == crs )
379  return;
380 
381  // try to reproject current extent to the new one
382  QgsRectangle rect;
383  if ( !mSettings.visibleExtent().isEmpty() )
384  {
385  QgsCoordinateTransform transform( mSettings.destinationCrs(), crs, QgsProject::instance() );
386  try
387  {
388  rect = transform.transformBoundingBox( mSettings.visibleExtent() );
389  }
390  catch ( QgsCsException &e )
391  {
392  Q_UNUSED( e )
393  QgsDebugMsg( QStringLiteral( "Transform error caught: %1" ).arg( e.what() ) );
394  }
395  }
396 
397  if ( !rect.isEmpty() )
398  {
399  setExtent( rect );
400  }
401 
402  mSettings.setDestinationCrs( crs );
403  updateScale();
404 
405  QgsDebugMsg( QStringLiteral( "refreshing after destination CRS changed" ) );
406  refresh();
407 
408  emit destinationCrsChanged();
409 }
410 
411 void QgsMapCanvas::setMapSettingsFlags( QgsMapSettings::Flags flags )
412 {
413  mSettings.setFlags( flags );
414  clearCache();
415  refresh();
416 }
417 
419 {
420  return mLabelingResults;
421 }
422 
424 {
425  if ( enabled == isCachingEnabled() )
426  return;
427 
428  if ( mJob && mJob->isActive() )
429  {
430  // wait for the current rendering to finish, before touching the cache
431  mJob->waitForFinished();
432  }
433 
434  if ( enabled )
435  {
436  mCache = new QgsMapRendererCache;
437  }
438  else
439  {
440  delete mCache;
441  mCache = nullptr;
442  }
443 }
444 
446 {
447  return nullptr != mCache;
448 }
449 
451 {
452  if ( mCache )
453  mCache->clear();
454 }
455 
457 {
458  mUseParallelRendering = enabled;
459 }
460 
462 {
463  return mUseParallelRendering;
464 }
465 
466 void QgsMapCanvas::setMapUpdateInterval( int timeMilliseconds )
467 {
468  mMapUpdateTimer.setInterval( timeMilliseconds );
469 }
470 
472 {
473  return mMapUpdateTimer.interval();
474 }
475 
476 
478 {
479  return mCurrentLayer;
480 }
481 
483 {
484  QgsExpressionContextScope *s = new QgsExpressionContextScope( QObject::tr( "Map Canvas" ) );
485  s->setVariable( QStringLiteral( "canvas_cursor_point" ), QgsGeometry::fromPointXY( cursorPoint() ), true );
486 
487  return s;
488 }
489 
491 {
492  if ( !mSettings.hasValidSettings() )
493  {
494  QgsDebugMsg( QStringLiteral( "CANVAS refresh - invalid settings -> nothing to do" ) );
495  return;
496  }
497 
498  if ( !mRenderFlag || mFrozen )
499  {
500  QgsDebugMsg( QStringLiteral( "CANVAS render flag off" ) );
501  return;
502  }
503 
504  if ( mRefreshScheduled )
505  {
506  QgsDebugMsg( QStringLiteral( "CANVAS refresh already scheduled" ) );
507  return;
508  }
509 
510  mRefreshScheduled = true;
511 
512  QgsDebugMsg( QStringLiteral( "CANVAS refresh scheduling" ) );
513 
514  // schedule a refresh
515  mRefreshTimer->start( 1 );
516 }
517 
518 void QgsMapCanvas::refreshMap()
519 {
520  Q_ASSERT( mRefreshScheduled );
521 
522  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh!" ), 3 );
523 
524  stopRendering(); // if any...
525  stopPreviewJobs();
526 
527  //build the expression context
528  QgsExpressionContext expressionContext;
529  expressionContext << QgsExpressionContextUtils::globalScope()
534  << new QgsExpressionContextScope( mExpressionContextScope );
535 
536  mSettings.setExpressionContext( expressionContext );
537  mSettings.setPathResolver( QgsProject::instance()->pathResolver() );
538 
539  if ( !mTheme.isEmpty() )
540  {
541  // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
542  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
543  // current state of the style. If we had stored the style overrides earlier (such as in
544  // mapThemeChanged slot) then this xml could be out of date...
545  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
546  // just return the style name, we can instead set the overrides in mapThemeChanged and not here
547  mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
548  }
549 
550  // create the renderer job
551  Q_ASSERT( !mJob );
552  mJobCanceled = false;
553  if ( mUseParallelRendering )
554  mJob = new QgsMapRendererParallelJob( mSettings );
555  else
556  mJob = new QgsMapRendererSequentialJob( mSettings );
557  connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
558  mJob->setCache( mCache );
559 
560  mJob->start();
561 
562  // from now on we can accept refresh requests again
563  // this must be reset only after the job has been started, because
564  // some providers (yes, it's you WCS and AMS!) during preparation
565  // do network requests and start an internal event loop, which may
566  // end up calling refresh() and would schedule another refresh,
567  // deleting the one we have just started.
568  mRefreshScheduled = false;
569 
570  mMapUpdateTimer.start();
571 
572  emit renderStarting();
573 }
574 
575 void QgsMapCanvas::mapThemeChanged( const QString &theme )
576 {
577  if ( theme == mTheme )
578  {
579  // set the canvas layers to match the new layers contained in the map theme
580  // NOTE: we do this when the theme layers change and not when we are refreshing the map
581  // as setLayers() sets up necessary connections to handle changes to the layers
582  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
583  // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
584  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
585  // current state of the style. If changes were made to the style then this xml
586  // snapshot goes out of sync...
587  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
588  // just return the style name, we can instead set the overrides here and not in refreshMap()
589 
590  clearCache();
591  refresh();
592  }
593 }
594 
595 void QgsMapCanvas::rendererJobFinished()
596 {
597  QgsDebugMsg( QStringLiteral( "CANVAS finish! %1" ).arg( !mJobCanceled ) );
598 
599  mMapUpdateTimer.stop();
600 
601  // TODO: would be better to show the errors in message bar
602  const auto constErrors = mJob->errors();
603  for ( const QgsMapRendererJob::Error &error : constErrors )
604  {
605  QgsMapLayer *layer = QgsProject::instance()->mapLayer( error.layerID );
606  emit renderErrorOccurred( error.message, layer );
607  QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) );
608  }
609 
610  if ( !mJobCanceled )
611  {
612  // take labeling results before emitting renderComplete, so labeling map tools
613  // connected to signal work with correct results
614  if ( !mJob->usedCachedLabels() )
615  {
616  delete mLabelingResults;
617  mLabelingResults = mJob->takeLabelingResults();
618  }
619 
620  QImage img = mJob->renderedImage();
621 
622  // emit renderComplete to get our decorations drawn
623  QPainter p( &img );
624  emit renderComplete( &p );
625 
626  QgsSettings settings;
627  if ( settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
628  {
629  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
630  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
631  }
632 
633  if ( mDrawRenderingStats )
634  {
635  int w = img.width(), h = img.height();
636  QFont fnt = p.font();
637  fnt.setBold( true );
638  p.setFont( fnt );
639  int lh = p.fontMetrics().height() * 2;
640  QRect r( 0, h - lh, w, lh );
641  p.setPen( Qt::NoPen );
642  p.setBrush( QColor( 0, 0, 0, 110 ) );
643  p.drawRect( r );
644  p.setPen( Qt::white );
645  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? QStringLiteral( "PARALLEL" ) : QStringLiteral( "SEQUENTIAL" ) ).arg( mJob->renderingTime() );
646  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
647  }
648 
649  p.end();
650 
651  mMap->setContent( img, imageRect( img, mSettings ) );
652 
653  mLastLayerRenderTime.clear();
654  const auto times = mJob->perLayerRenderingTime();
655  for ( auto it = times.constBegin(); it != times.constEnd(); ++it )
656  {
657  mLastLayerRenderTime.insert( it.key()->id(), it.value() );
658  }
659  if ( mUsePreviewJobs )
660  startPreviewJobs();
661  }
662 
663  // now we are in a slot called from mJob - do not delete it immediately
664  // so the class is still valid when the execution returns to the class
665  mJob->deleteLater();
666  mJob = nullptr;
667 
668  emit mapCanvasRefreshed();
669 }
670 
671 void QgsMapCanvas::previewJobFinished()
672 {
673  QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
674  Q_ASSERT( job );
675 
676  if ( mMap )
677  {
678  mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
679  mPreviewJobs.removeAll( job );
680 
681  int number = job->property( "number" ).toInt();
682  if ( number < 8 )
683  {
684  startPreviewJob( number + 1 );
685  }
686 
687  delete job;
688  }
689 }
690 
691 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
692 {
693  // This is a hack to pass QgsMapCanvasItem::setRect what it
694  // expects (encoding of position and size of the item)
695  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
696  QgsPointXY topLeft = m2p.toMapCoordinates( 0, 0 );
697 #ifdef QGISDEBUG
698  // do not assert this, since it might lead to crashes when changing screen while rendering
699  if ( img.devicePixelRatio() != mapSettings.devicePixelRatio() )
700  {
701  QgsLogger::warning( QStringLiteral( "The renderer map has a wrong device pixel ratio" ) );
702  }
703 #endif
704  double res = m2p.mapUnitsPerPixel() / img.devicePixelRatioF();
705  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
706  return rect;
707 }
708 
710 {
711  return mUsePreviewJobs;
712 }
713 
715 {
716  mUsePreviewJobs = enabled;
717 }
718 
719 void QgsMapCanvas::mapUpdateTimeout()
720 {
721  if ( mJob )
722  {
723  const QImage &img = mJob->renderedImage();
724  mMap->setContent( img, imageRect( img, mSettings ) );
725  }
726 }
727 
729 {
730  if ( mJob )
731  {
732  QgsDebugMsg( QStringLiteral( "CANVAS stop rendering!" ) );
733  mJobCanceled = true;
734  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
735  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
736  mJob->cancelWithoutBlocking();
737  mJob = nullptr;
738  }
739  stopPreviewJobs();
740 }
741 
742 //the format defaults to "PNG" if not specified
743 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
744 {
745  QPainter painter;
746  QImage image;
747 
748  //
749  //check if the optional QPaintDevice was supplied
750  //
751  if ( theQPixmap )
752  {
753  image = theQPixmap->toImage();
754  painter.begin( &image );
755 
756  // render
757  QgsMapRendererCustomPainterJob job( mSettings, &painter );
758  job.start();
759  job.waitForFinished();
760  emit renderComplete( &painter );
761  }
762  else //use the map view
763  {
764  image = mMap->contentImage().copy();
765  painter.begin( &image );
766  }
767 
768  // draw annotations
769  QStyleOptionGraphicsItem option;
770  option.initFrom( this );
771  QGraphicsItem *item = nullptr;
772  QListIterator<QGraphicsItem *> i( items() );
773  i.toBack();
774  while ( i.hasPrevious() )
775  {
776  item = i.previous();
777 
778  if ( !( item && dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) ) )
779  {
780  continue;
781  }
782 
783  painter.save();
784 
785  QPointF itemScenePos = item->scenePos();
786  painter.translate( itemScenePos.x(), itemScenePos.y() );
787 
788  item->paint( &painter, &option );
789 
790  painter.restore();
791  }
792 
793  painter.end();
794  image.save( fileName, format.toLocal8Bit().data() );
795 
796  QFileInfo myInfo = QFileInfo( fileName );
797 
798  // build the world file name
799  QString outputSuffix = myInfo.suffix();
800  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
801  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
802  QFile myWorldFile( myWorldFileName );
803  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
804  {
805  return;
806  }
807  QTextStream myStream( &myWorldFile );
809 } // saveAsImage
810 
811 
812 
814 {
815  return mapSettings().visibleExtent();
816 } // extent
817 
819 {
820  return mapSettings().fullExtent();
821 } // extent
822 
823 
824 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
825 {
826  QgsRectangle current = extent();
827 
828  if ( ( r == current ) && magnified )
829  return;
830 
831  if ( r.isEmpty() )
832  {
833  if ( !mSettings.hasValidSettings() )
834  {
835  // we can't even just move the map center
836  QgsDebugMsg( QStringLiteral( "Empty extent - ignoring" ) );
837  return;
838  }
839 
840  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
841  QgsDebugMsg( QStringLiteral( "Empty extent - keeping old scale with new center!" ) );
842  setCenter( r.center() );
843  }
844  else
845  {
846  mSettings.setExtent( r, magnified );
847  }
848  emit extentsChanged();
849  updateScale();
850  if ( mLastExtent.size() > 20 )
851  mLastExtent.removeAt( 0 );
852 
853  //clear all extent items after current index
854  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
855  {
856  mLastExtent.removeAt( i );
857  }
858 
859  mLastExtent.append( extent() );
860 
861  // adjust history to no more than 20
862  if ( mLastExtent.size() > 20 )
863  {
864  mLastExtent.removeAt( 0 );
865  }
866 
867  // the last item is the current extent
868  mLastExtentIndex = mLastExtent.size() - 1;
869 
870  // update controls' enabled state
871  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
872  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
873 } // setExtent
874 
876 {
878  double x = center.x();
879  double y = center.y();
880  setExtent(
881  QgsRectangle(
882  x - r.width() / 2.0, y - r.height() / 2.0,
883  x + r.width() / 2.0, y + r.height() / 2.0
884  ),
885  true
886  );
887 } // setCenter
888 
890 {
892  return r.center();
893 }
894 
895 QgsPointXY QgsMapCanvas::cursorPoint() const
896 {
897  return mCursorPoint;
898 }
899 
901 {
902  return mapSettings().rotation();
903 } // rotation
904 
905 void QgsMapCanvas::setRotation( double degrees )
906 {
907  double current = rotation();
908 
909  if ( qgsDoubleNear( degrees, current ) )
910  return;
911 
912  mSettings.setRotation( degrees );
913  emit rotationChanged( degrees );
914  emit extentsChanged(); // visible extent changes with rotation
915 } // setRotation
916 
917 
919 {
920  emit scaleChanged( mapSettings().scale() );
921 }
922 
923 
925 {
927  // If the full extent is an empty set, don't do the zoom
928  if ( !extent.isEmpty() )
929  {
930  // Add a 5% margin around the full extent
931  extent.scale( 1.05 );
932  setExtent( extent );
933  }
934  refresh();
935 
936 } // zoomToFullExtent
937 
938 
940 {
941  if ( mLastExtentIndex > 0 )
942  {
943  mLastExtentIndex--;
944  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
945  emit extentsChanged();
946  updateScale();
947  refresh();
948  // update controls' enabled state
949  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
950  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
951  }
952 
953 } // zoomToPreviousExtent
954 
956 {
957  if ( mLastExtentIndex < mLastExtent.size() - 1 )
958  {
959  mLastExtentIndex++;
960  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
961  emit extentsChanged();
962  updateScale();
963  refresh();
964  // update controls' enabled state
965  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
966  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
967  }
968 }// zoomToNextExtent
969 
971 {
972  mLastExtent.clear(); // clear the zoom history list
973  mLastExtent.append( extent() ) ; // set the current extent in the list
974  mLastExtentIndex = mLastExtent.size() - 1;
975  // update controls' enabled state
976  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
977  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
978 }// clearExtentHistory
979 
981 {
982  if ( !layer )
983  {
984  // use current layer by default
985  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
986  }
987 
988  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
989  return;
990 
991  QgsRectangle rect = layer->boundingBoxOfSelected();
992  if ( rect.isNull() )
993  {
994  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
995  return;
996  }
997 
998  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
999 
1000  // zoom in if point cannot be distinguished from others
1001  // also check that rect is empty, as it might not in case of multi points
1002  if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1003  {
1004  int scaleFactor = 5;
1005  QgsPointXY center = mSettings.mapToLayerCoordinates( layer, rect.center() );
1006  QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, &center );
1008  QgsFeatureIterator fit = layer->getFeatures( req );
1009  QgsFeature f;
1010  QgsPointXY closestPoint;
1011  double closestSquaredDistance = pow( extentRect.width(), 2.0 ) + pow( extentRect.height(), 2.0 );
1012  bool pointFound = false;
1013  while ( fit.nextFeature( f ) )
1014  {
1015  QgsPointXY point = f.geometry().asPoint();
1016  double sqrDist = point.sqrDist( center );
1017  if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits<double>::epsilon() )
1018  continue;
1019  pointFound = true;
1020  closestPoint = point;
1021  closestSquaredDistance = sqrDist;
1022  }
1023  if ( pointFound )
1024  {
1025  // combine selected point with closest point and scale this rect
1026  rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) );
1027  rect.scale( scaleFactor, &center );
1028  }
1029  }
1030 
1031  zoomToFeatureExtent( rect );
1032 }
1033 
1035 {
1036  // no selected features, only one selected point feature
1037  //or two point features with the same x- or y-coordinates
1038  if ( rect.isEmpty() )
1039  {
1040  // zoom in
1041  QgsPointXY c = rect.center();
1042  rect = extent();
1043  rect.scale( 1.0, &c );
1044  }
1045  //zoom to an area
1046  else
1047  {
1048  // Expand rect to give a bit of space around the selected
1049  // objects so as to keep them clear of the map boundaries
1050  // The same 5% should apply to all margins.
1051  rect.scale( 1.05 );
1052  }
1053 
1054  setExtent( rect );
1055  refresh();
1056 }
1057 
1059 {
1060  if ( !layer )
1061  {
1062  return;
1063  }
1064 
1065  QgsRectangle bbox;
1066  QString errorMsg;
1067  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1068  {
1069  zoomToFeatureExtent( bbox );
1070  }
1071  else
1072  {
1073  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, Qgis::Warning );
1074  }
1075 
1076 }
1077 
1078 void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter )
1079 {
1080  if ( !layer )
1081  {
1082  return;
1083  }
1084 
1085  QgsRectangle bbox;
1086  QString errorMsg;
1087  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1088  {
1089  if ( alwaysRecenter || !mapSettings().extent().contains( bbox ) )
1090  setCenter( bbox.center() );
1091  refresh();
1092  }
1093  else
1094  {
1095  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, Qgis::Warning );
1096  }
1097 }
1098 
1099 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1100 {
1101  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1102  bbox.setMinimal();
1103  QgsFeature fet;
1104  int featureCount = 0;
1105  errorMsg.clear();
1106 
1107  while ( it.nextFeature( fet ) )
1108  {
1109  QgsGeometry geom = fet.geometry();
1110  if ( geom.isNull() )
1111  {
1112  errorMsg = tr( "Feature does not have a geometry" );
1113  }
1114  else if ( geom.constGet()->isEmpty() )
1115  {
1116  errorMsg = tr( "Feature geometry is empty" );
1117  }
1118  if ( !errorMsg.isEmpty() )
1119  {
1120  return false;
1121  }
1123  bbox.combineExtentWith( r );
1124  featureCount++;
1125  }
1126 
1127  if ( featureCount != ids.count() )
1128  {
1129  errorMsg = tr( "Feature not found" );
1130  return false;
1131  }
1132 
1133  return true;
1134 }
1135 
1137 {
1138  if ( !layer )
1139  {
1140  // use current layer by default
1141  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1142  }
1143 
1144  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1145  return;
1146 
1147  QgsRectangle rect = layer->boundingBoxOfSelected();
1148  if ( rect.isNull() )
1149  {
1150  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
1151  return;
1152  }
1153 
1154  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1155  setCenter( rect.center() );
1156  refresh();
1157 }
1158 
1160  const QColor &color1, const QColor &color2,
1161  int flashes, int duration )
1162 {
1163  if ( !layer )
1164  {
1165  return;
1166  }
1167 
1168  QList< QgsGeometry > geoms;
1169 
1170  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1171  QgsFeature fet;
1172  while ( it.nextFeature( fet ) )
1173  {
1174  if ( !fet.hasGeometry() )
1175  continue;
1176  geoms << fet.geometry();
1177  }
1178 
1179  flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
1180 }
1181 
1182 void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
1183 {
1184  if ( geometries.isEmpty() )
1185  return;
1186 
1187  QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
1188  QgsRubberBand *rb = new QgsRubberBand( this, geomType );
1189  for ( const QgsGeometry &geom : geometries )
1190  rb->addGeometry( geom, crs );
1191 
1192  if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
1193  {
1194  rb->setWidth( 2 );
1195  rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
1196  }
1197  if ( geomType == QgsWkbTypes::PointGeometry )
1198  rb->setIcon( QgsRubberBand::ICON_CIRCLE );
1199 
1200  QColor startColor = color1;
1201  if ( !startColor.isValid() )
1202  {
1203  if ( geomType == QgsWkbTypes::PolygonGeometry )
1204  {
1205  startColor = rb->fillColor();
1206  }
1207  else
1208  {
1209  startColor = rb->strokeColor();
1210  }
1211  startColor.setAlpha( 255 );
1212  }
1213  QColor endColor = color2;
1214  if ( !endColor.isValid() )
1215  {
1216  endColor = startColor;
1217  endColor.setAlpha( 0 );
1218  }
1219 
1220 
1221  QVariantAnimation *animation = new QVariantAnimation( this );
1222  connect( animation, &QVariantAnimation::finished, this, [animation, rb]
1223  {
1224  animation->deleteLater();
1225  delete rb;
1226  } );
1227  connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
1228  {
1229  QColor c = value.value<QColor>();
1230  if ( geomType == QgsWkbTypes::PolygonGeometry )
1231  {
1232  rb->setFillColor( c );
1233  }
1234  else
1235  {
1236  rb->setStrokeColor( c );
1237  QColor c = rb->secondaryStrokeColor();
1238  c.setAlpha( c.alpha() );
1239  rb->setSecondaryStrokeColor( c );
1240  }
1241  rb->update();
1242  } );
1243 
1244  animation->setDuration( duration * flashes );
1245  animation->setStartValue( endColor );
1246  double midStep = 0.2 / flashes;
1247  for ( int i = 0; i < flashes; ++i )
1248  {
1249  double start = static_cast< double >( i ) / flashes;
1250  animation->setKeyValueAt( start + midStep, startColor );
1251  double end = static_cast< double >( i + 1 ) / flashes;
1252  if ( !qgsDoubleNear( end, 1.0 ) )
1253  animation->setKeyValueAt( end, endColor );
1254  }
1255  animation->setEndValue( endColor );
1256  animation->start();
1257 }
1258 
1259 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1260 {
1261  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1262  {
1263  emit keyPressed( e );
1264  return;
1265  }
1266 
1267  if ( ! mCanvasProperties->mouseButtonDown )
1268  {
1269  // Don't want to interfer with mouse events
1270 
1271  QgsRectangle currentExtent = mapSettings().visibleExtent();
1272  double dx = std::fabs( currentExtent.width() / 4 );
1273  double dy = std::fabs( currentExtent.height() / 4 );
1274 
1275  switch ( e->key() )
1276  {
1277  case Qt::Key_Left:
1278  QgsDebugMsg( QStringLiteral( "Pan left" ) );
1279  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1280  refresh();
1281  break;
1282 
1283  case Qt::Key_Right:
1284  QgsDebugMsg( QStringLiteral( "Pan right" ) );
1285  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1286  refresh();
1287  break;
1288 
1289  case Qt::Key_Up:
1290  QgsDebugMsg( QStringLiteral( "Pan up" ) );
1291  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1292  refresh();
1293  break;
1294 
1295  case Qt::Key_Down:
1296  QgsDebugMsg( QStringLiteral( "Pan down" ) );
1297  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1298  refresh();
1299  break;
1300 
1301 
1302 
1303  case Qt::Key_Space:
1304  QgsDebugMsg( QStringLiteral( "Pressing pan selector" ) );
1305 
1306  //mCanvasProperties->dragging = true;
1307  if ( ! e->isAutoRepeat() )
1308  {
1309  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1310  mCanvasProperties->panSelectorDown = true;
1311  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1312  }
1313  break;
1314 
1315  case Qt::Key_PageUp:
1316  QgsDebugMsg( QStringLiteral( "Zoom in" ) );
1317  zoomIn();
1318  break;
1319 
1320  case Qt::Key_PageDown:
1321  QgsDebugMsg( QStringLiteral( "Zoom out" ) );
1322  zoomOut();
1323  break;
1324 
1325 #if 0
1326  case Qt::Key_P:
1327  mUseParallelRendering = !mUseParallelRendering;
1328  refresh();
1329  break;
1330 
1331  case Qt::Key_S:
1332  mDrawRenderingStats = !mDrawRenderingStats;
1333  refresh();
1334  break;
1335 #endif
1336 
1337  default:
1338  // Pass it on
1339  if ( mMapTool )
1340  {
1341  mMapTool->keyPressEvent( e );
1342  }
1343  else e->ignore();
1344 
1345  QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
1346  }
1347  }
1348 
1349  emit keyPressed( e );
1350 
1351 } //keyPressEvent()
1352 
1353 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1354 {
1355  QgsDebugMsg( QStringLiteral( "keyRelease event" ) );
1356 
1357  switch ( e->key() )
1358  {
1359  case Qt::Key_Space:
1360  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1361  {
1362  QgsDebugMsg( QStringLiteral( "Releasing pan selector" ) );
1363  QApplication::restoreOverrideCursor();
1364  mCanvasProperties->panSelectorDown = false;
1365  panActionEnd( mCanvasProperties->mouseLastXY );
1366  }
1367  break;
1368 
1369  default:
1370  // Pass it on
1371  if ( mMapTool )
1372  {
1373  mMapTool->keyReleaseEvent( e );
1374  }
1375  else e->ignore();
1376 
1377  QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
1378  }
1379 
1380  emit keyReleased( e );
1381 
1382 } //keyReleaseEvent()
1383 
1384 
1386 {
1387  // call handler of current map tool
1388  if ( mMapTool )
1389  {
1390  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1391  mMapTool->canvasDoubleClickEvent( me.get() );
1392  }
1393 }// mouseDoubleClickEvent
1394 
1395 
1396 void QgsMapCanvas::beginZoomRect( QPoint pos )
1397 {
1398  mZoomRect.setRect( 0, 0, 0, 0 );
1399  QApplication::setOverrideCursor( mZoomCursor );
1400  mZoomDragging = true;
1401  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1402  QColor color( Qt::blue );
1403  color.setAlpha( 63 );
1404  mZoomRubberBand->setColor( color );
1405  mZoomRect.setTopLeft( pos );
1406 }
1407 
1408 void QgsMapCanvas::endZoomRect( QPoint pos )
1409 {
1410  mZoomDragging = false;
1411  mZoomRubberBand.reset( nullptr );
1412  QApplication::restoreOverrideCursor();
1413 
1414  // store the rectangle
1415  mZoomRect.setRight( pos.x() );
1416  mZoomRect.setBottom( pos.y() );
1417 
1418  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1419  {
1420  //probably a mistake - would result in huge zoom!
1421  return;
1422  }
1423 
1424  //account for bottom right -> top left dragging
1425  mZoomRect = mZoomRect.normalized();
1426 
1427  // set center and zoom
1428  const QSize &zoomRectSize = mZoomRect.size();
1429  const QSize &canvasSize = mSettings.outputSize();
1430  double sfx = static_cast< double >( zoomRectSize.width() ) / canvasSize.width();
1431  double sfy = static_cast< double >( zoomRectSize.height() ) / canvasSize.height();
1432  double sf = std::max( sfx, sfy );
1433 
1434  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1435 
1436  zoomByFactor( sf, &c );
1437  refresh();
1438 }
1439 
1440 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1441 {
1442  //use middle mouse button for panning, map tools won't receive any events in that case
1443  if ( e->button() == Qt::MidButton )
1444  {
1445  mCanvasProperties->panSelectorDown = true;
1446  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1447  }
1448  else
1449  {
1450  // call handler of current map tool
1451  if ( mMapTool )
1452  {
1453  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1454  && e->modifiers() & Qt::ShiftModifier )
1455  {
1456  beginZoomRect( e->pos() );
1457  return;
1458  }
1459  else
1460  {
1461  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1462  mMapTool->canvasPressEvent( me.get() );
1463  }
1464  }
1465  }
1466 
1467  if ( mCanvasProperties->panSelectorDown )
1468  {
1469  return;
1470  }
1471 
1472  mCanvasProperties->mouseButtonDown = true;
1473  mCanvasProperties->rubberStartPoint = e->pos();
1474 
1475 } // mousePressEvent
1476 
1477 
1478 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1479 {
1480  //use middle mouse button for panning, map tools won't receive any events in that case
1481  if ( e->button() == Qt::MidButton )
1482  {
1483  mCanvasProperties->panSelectorDown = false;
1484  panActionEnd( mCanvasProperties->mouseLastXY );
1485  }
1486  else if ( e->button() == Qt::BackButton )
1487  {
1489  return;
1490  }
1491  else if ( e->button() == Qt::ForwardButton )
1492  {
1493  zoomToNextExtent();
1494  return;
1495  }
1496  else
1497  {
1498  if ( mZoomDragging && e->button() == Qt::LeftButton )
1499  {
1500  endZoomRect( e->pos() );
1501  return;
1502  }
1503 
1504  // call handler of current map tool
1505  if ( mMapTool )
1506  {
1507  // right button was pressed in zoom tool? return to previous non zoom tool
1508  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1509  {
1510  QgsDebugMsg( QStringLiteral( "Right click in map tool zoom or pan, last tool is %1." ).arg(
1511  mLastNonZoomMapTool ? QStringLiteral( "not null" ) : QStringLiteral( "null" ) ) );
1512 
1513  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1514 
1515  // change to older non-zoom tool
1516  if ( mLastNonZoomMapTool
1517  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1518  || ( vlayer && vlayer->isEditable() ) ) )
1519  {
1520  QgsMapTool *t = mLastNonZoomMapTool;
1521  mLastNonZoomMapTool = nullptr;
1522  setMapTool( t );
1523  }
1524  return;
1525  }
1526  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1527  mMapTool->canvasReleaseEvent( me.get() );
1528  }
1529  }
1530 
1531 
1532  mCanvasProperties->mouseButtonDown = false;
1533 
1534  if ( mCanvasProperties->panSelectorDown )
1535  return;
1536 
1537 } // mouseReleaseEvent
1538 
1539 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1540 {
1541  QGraphicsView::resizeEvent( e );
1542  mResizeTimer->start( 500 ); // in charge of refreshing canvas
1543 
1544  double oldScale = mSettings.scale();
1545  QSize lastSize = viewport()->size();
1546  mSettings.setOutputSize( lastSize );
1547 
1548  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1549 
1550  moveCanvasContents( true );
1551 
1552  if ( mScaleLocked )
1553  {
1554  double scaleFactor = oldScale / mSettings.scale();
1555  QgsRectangle r = mSettings.extent();
1556  QgsPointXY center = r.center();
1557  r.scale( scaleFactor, &center );
1558  mSettings.setExtent( r );
1559  }
1560  else
1561  {
1562  updateScale();
1563  }
1564 
1565  emit extentsChanged();
1566 }
1567 
1568 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1569 {
1570  // no custom event handling anymore
1571 
1572  QGraphicsView::paintEvent( e );
1573 } // paintEvent
1574 
1576 {
1577  const QList<QGraphicsItem *> items = mScene->items();
1578  for ( QGraphicsItem *gi : items )
1579  {
1580  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( gi );
1581 
1582  if ( item )
1583  {
1584  item->updatePosition();
1585  }
1586  }
1587 }
1588 
1589 
1590 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1591 {
1592  // Zoom the map canvas in response to a mouse wheel event. Moving the
1593  // wheel forward (away) from the user zooms in
1594 
1595  QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
1596 
1597  if ( mMapTool )
1598  {
1599  mMapTool->wheelEvent( e );
1600  if ( e->isAccepted() )
1601  return;
1602  }
1603 
1604  if ( e->delta() == 0 )
1605  {
1606  e->accept();
1607  return;
1608  }
1609 
1610  double zoomFactor = mWheelZoomFactor;
1611 
1612  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1613  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
1614 
1615  if ( e->modifiers() & Qt::ControlModifier )
1616  {
1617  //holding ctrl while wheel zooming results in a finer zoom
1618  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1619  }
1620 
1621  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1622 
1623  // zoom map to mouse cursor by scaling
1624  QgsPointXY oldCenter = center();
1625  QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->x(), e->y() ) );
1626  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1627  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1628 
1629  zoomByFactor( signedWheelFactor, &newCenter );
1630  e->accept();
1631 }
1632 
1633 void QgsMapCanvas::setWheelFactor( double factor )
1634 {
1635  mWheelZoomFactor = factor;
1636 }
1637 
1639 {
1640  // magnification is alreday handled in zoomByFactor
1641  zoomByFactor( 1 / mWheelZoomFactor );
1642 }
1643 
1645 {
1646  // magnification is alreday handled in zoomByFactor
1647  zoomByFactor( mWheelZoomFactor );
1648 }
1649 
1650 void QgsMapCanvas::zoomScale( double newScale )
1651 {
1652  zoomByFactor( newScale / scale() );
1653 }
1654 
1655 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1656 {
1657  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1658 
1659  if ( mScaleLocked )
1660  {
1662  }
1663  else
1664  {
1665  // transform the mouse pos to map coordinates
1668  r.scale( scaleFactor, &center );
1669  setExtent( r, true );
1670  refresh();
1671  }
1672 }
1673 
1674 void QgsMapCanvas::setScaleLocked( bool isLocked )
1675 {
1676  mScaleLocked = isLocked;
1677 }
1678 
1679 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1680 {
1681  mCanvasProperties->mouseLastXY = e->pos();
1682 
1683  if ( mCanvasProperties->panSelectorDown )
1684  {
1685  panAction( e );
1686  }
1687  else if ( mZoomDragging )
1688  {
1689  mZoomRect.setBottomRight( e->pos() );
1690  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1691  mZoomRubberBand->show();
1692  }
1693  else
1694  {
1695  // call handler of current map tool
1696  if ( mMapTool )
1697  {
1698  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1699  mMapTool->canvasMoveEvent( me.get() );
1700  }
1701  }
1702 
1703  // show x y on status bar
1704  mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
1705  emit xyCoordinates( mCursorPoint );
1706 }
1707 
1708 void QgsMapCanvas::setMapTool( QgsMapTool *tool, bool clean )
1709 {
1710  if ( !tool )
1711  return;
1712 
1713  if ( mMapTool )
1714  {
1715  if ( clean )
1716  mMapTool->clean();
1717 
1718  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1719  mMapTool->deactivate();
1720  }
1721 
1722  if ( ( tool->flags() & QgsMapTool::Transient )
1723  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1724  {
1725  // if zoom or pan tool will be active, save old tool
1726  // to bring it back on right click
1727  // (but only if it wasn't also zoom or pan tool)
1728  mLastNonZoomMapTool = mMapTool;
1729  }
1730  else
1731  {
1732  mLastNonZoomMapTool = nullptr;
1733  }
1734 
1735  QgsMapTool *oldTool = mMapTool;
1736 
1737  // set new map tool and activate it
1738  mMapTool = tool;
1739  if ( mMapTool )
1740  {
1741  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1742  mMapTool->activate();
1743  }
1744 
1745  emit mapToolSet( mMapTool, oldTool );
1746 } // setMapTool
1747 
1749 {
1750  if ( mMapTool && mMapTool == tool )
1751  {
1752  mMapTool->deactivate();
1753  mMapTool = nullptr;
1754  emit mapToolSet( nullptr, mMapTool );
1755  setCursor( Qt::ArrowCursor );
1756  }
1757 
1758  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1759  {
1760  mLastNonZoomMapTool = nullptr;
1761  }
1762 }
1763 
1764 void QgsMapCanvas::setCanvasColor( const QColor &color )
1765 {
1766  if ( canvasColor() == color )
1767  return;
1768 
1769  // background of map's pixmap
1770  mSettings.setBackgroundColor( color );
1771 
1772  // background of the QGraphicsView
1773  QBrush bgBrush( color );
1774  setBackgroundBrush( bgBrush );
1775 #if 0
1776  QPalette palette;
1777  palette.setColor( backgroundRole(), color );
1778  setPalette( palette );
1779 #endif
1780 
1781  // background of QGraphicsScene
1782  mScene->setBackgroundBrush( bgBrush );
1783 
1784  emit canvasColorChanged();
1785 }
1786 
1788 {
1789  return mScene->backgroundBrush().color();
1790 }
1791 
1792 void QgsMapCanvas::setSelectionColor( const QColor &color )
1793 {
1794  mSettings.setSelectionColor( color );
1795 }
1796 
1798 {
1799  return mSettings.selectionColor();
1800 }
1801 
1803 {
1804  return mapSettings().layers().size();
1805 } // layerCount
1806 
1807 
1808 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1809 {
1810  return mapSettings().layers();
1811 }
1812 
1814 {
1815  // called when a layer has changed visibility setting
1816  refresh();
1817 }
1818 
1819 void QgsMapCanvas::freeze( bool frozen )
1820 {
1821  mFrozen = frozen;
1822 }
1823 
1825 {
1826  return mFrozen;
1827 }
1828 
1830 {
1831  return mapSettings().mapUnitsPerPixel();
1832 }
1833 
1835 {
1836  return mapSettings().mapUnits();
1837 }
1838 
1839 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1840 {
1841  return mSettings.layerStyleOverrides();
1842 }
1843 
1844 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1845 {
1846  if ( overrides == mSettings.layerStyleOverrides() )
1847  return;
1848 
1849  mSettings.setLayerStyleOverrides( overrides );
1850  clearCache();
1852 }
1853 
1854 void QgsMapCanvas::setTheme( const QString &theme )
1855 {
1856  if ( mTheme == theme )
1857  return;
1858 
1859  clearCache();
1860  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1861  {
1862  mTheme.clear();
1863  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1864  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1865  emit themeChanged( QString() );
1866  }
1867  else
1868  {
1869  mTheme = theme;
1870  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1871  emit themeChanged( theme );
1872  }
1873 }
1874 
1876 {
1877  mRenderFlag = flag;
1878 
1879  if ( mRenderFlag )
1880  {
1881  refresh();
1882  }
1883  else
1884  stopRendering();
1885 }
1886 
1887 #if 0
1888 void QgsMapCanvas::connectNotify( const char *signal )
1889 {
1890  Q_UNUSED( signal )
1891  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1892 } //connectNotify
1893 #endif
1894 
1895 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1896 {
1897  if ( !deferred )
1898  refresh();
1899 }
1900 
1901 void QgsMapCanvas::autoRefreshTriggered()
1902 {
1903  if ( mJob )
1904  {
1905  // canvas is currently being redrawn, so we skip this auto refresh
1906  // otherwise we could get stuck in the situation where an auto refresh is triggered
1907  // too often to allow the canvas to ever finish rendering
1908  return;
1909  }
1910 
1911  refresh();
1912 }
1913 
1914 void QgsMapCanvas::updateAutoRefreshTimer()
1915 {
1916  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1917  // trigger a map refresh on this minimum interval
1918  int minAutoRefreshInterval = -1;
1919  const auto layers = mSettings.layers();
1920  for ( QgsMapLayer *layer : layers )
1921  {
1922  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1923  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1924  }
1925 
1926  if ( minAutoRefreshInterval > 0 )
1927  {
1928  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1929  mAutoRefreshTimer.start();
1930  }
1931  else
1932  {
1933  mAutoRefreshTimer.stop();
1934  }
1935 }
1936 
1937 void QgsMapCanvas::projectThemesChanged()
1938 {
1939  if ( mTheme.isEmpty() )
1940  return;
1941 
1942  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1943  {
1944  // theme has been removed - stop following
1945  setTheme( QString() );
1946  }
1947 
1948 }
1949 
1951 {
1952  return mMapTool;
1953 }
1954 
1955 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
1956 {
1957  // move map image and other items to standard position
1958  moveCanvasContents( true ); // true means reset
1959 
1960  // use start and end box points to calculate the extent
1961  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
1962  QgsPointXY end = getCoordinateTransform()->toMapCoordinates( releasePoint );
1963 
1964  // modify the center
1965  double dx = end.x() - start.x();
1966  double dy = end.y() - start.y();
1967  QgsPointXY c = center();
1968  c.set( c.x() - dx, c.y() - dy );
1969  setCenter( c );
1970 
1971  refresh();
1972 }
1973 
1974 void QgsMapCanvas::panAction( QMouseEvent *e )
1975 {
1976  Q_UNUSED( e )
1977 
1978  // move all map canvas items
1980 }
1981 
1983 {
1984  QPoint pnt( 0, 0 );
1985  if ( !reset )
1986  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
1987 
1988  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
1989 }
1990 
1992 {
1993  return mCanvasProperties->mouseLastXY;
1994 }
1995 
1996 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
1997 {
1998  if ( !mPreviewEffect )
1999  {
2000  return;
2001  }
2002 
2003  mPreviewEffect->setEnabled( previewEnabled );
2004 }
2005 
2007 {
2008  if ( !mPreviewEffect )
2009  {
2010  return false;
2011  }
2012 
2013  return mPreviewEffect->isEnabled();
2014 }
2015 
2017 {
2018  if ( !mPreviewEffect )
2019  {
2020  return;
2021  }
2022 
2023  mPreviewEffect->setMode( mode );
2024 }
2025 
2027 {
2028  if ( !mPreviewEffect )
2029  {
2031  }
2032 
2033  return mPreviewEffect->mode();
2034 }
2035 
2037 {
2038  if ( !mSnappingUtils )
2039  {
2040  // associate a dummy instance, but better than null pointer
2041  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
2042  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
2043  }
2044  return mSnappingUtils;
2045 }
2046 
2048 {
2049  mSnappingUtils = utils;
2050 }
2051 
2052 void QgsMapCanvas::readProject( const QDomDocument &doc )
2053 {
2054  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
2055  if ( nodes.count() )
2056  {
2057  QDomNode node = nodes.item( 0 );
2058 
2059  // Search the specific MapCanvas node using the name
2060  if ( nodes.count() > 1 )
2061  {
2062  for ( int i = 0; i < nodes.size(); ++i )
2063  {
2064  QDomElement elementNode = nodes.at( i ).toElement();
2065 
2066  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
2067  {
2068  node = nodes.at( i );
2069  break;
2070  }
2071  }
2072  }
2073 
2074  QgsMapSettings tmpSettings;
2075  tmpSettings.readXml( node );
2076  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
2077  {
2078  // never manually set the crs for the main canvas - this is instead connected to the project CRS
2079  setDestinationCrs( tmpSettings.destinationCrs() );
2080  }
2081  setExtent( tmpSettings.extent() );
2082  setRotation( tmpSettings.rotation() );
2084 
2085  clearExtentHistory(); // clear the extent history on project load
2086 
2087  QDomElement elem = node.toElement();
2088  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
2089  {
2090  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
2091  {
2092  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
2093  }
2094  }
2095  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
2096 
2097  // restore canvas expression context
2098  const QDomNodeList scopeElements = elem.elementsByTagName( QStringLiteral( "expressionContextScope" ) );
2099  if ( scopeElements.size() > 0 )
2100  {
2101  const QDomElement scopeElement = scopeElements.at( 0 ).toElement();
2102  mExpressionContextScope.readXml( scopeElement, QgsReadWriteContext() );
2103  }
2104  }
2105  else
2106  {
2107  QgsDebugMsg( QStringLiteral( "Couldn't read mapcanvas information from project" ) );
2108  }
2109 }
2110 
2111 void QgsMapCanvas::writeProject( QDomDocument &doc )
2112 {
2113  // create node "mapcanvas" and call mMapRenderer->writeXml()
2114 
2115  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
2116  if ( !nl.count() )
2117  {
2118  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
2119  return;
2120  }
2121  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
2122 
2123  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
2124  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
2125  if ( !mTheme.isEmpty() )
2126  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
2127  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
2128  qgisNode.appendChild( mapcanvasNode );
2129 
2130  mSettings.writeXml( mapcanvasNode, doc );
2131 
2132  // store canvas expression context
2133  QDomElement scopeElement = doc.createElement( QStringLiteral( "expressionContextScope" ) );
2134  QgsExpressionContextScope tmpScope( mExpressionContextScope );
2135  tmpScope.removeVariable( QStringLiteral( "atlas_featurenumber" ) );
2136  tmpScope.removeVariable( QStringLiteral( "atlas_pagename" ) );
2137  tmpScope.removeVariable( QStringLiteral( "atlas_feature" ) );
2138  tmpScope.removeVariable( QStringLiteral( "atlas_featureid" ) );
2139  tmpScope.removeVariable( QStringLiteral( "atlas_geometry" ) );
2140  tmpScope.writeXml( scopeElement, doc, QgsReadWriteContext() );
2141  mapcanvasNode.appendChild( scopeElement );
2142 
2143  // TODO: store only units, extent, projections, dest CRS
2144 }
2145 
2146 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center )
2147 {
2148  if ( mScaleLocked )
2149  {
2150  // zoom map to mouse cursor by magnifying
2152  }
2153  else
2154  {
2156  r.scale( scaleFactor, center );
2157  setExtent( r, true );
2158  refresh();
2159  }
2160 }
2161 
2163 {
2164  // Find out which layer it was that sent the signal.
2165  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( sender() );
2166  if ( layer )
2167  {
2168  emit selectionChanged( layer );
2169  refresh();
2170  }
2171 }
2172 
2173 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *e )
2174 {
2175  // By default graphics view delegates the drag events to graphics items.
2176  // But we do not want that and by ignoring the drag enter we let the
2177  // parent (e.g. QgisApp) to handle drops of map layers etc.
2178  e->ignore();
2179 }
2180 
2181 void QgsMapCanvas::mapToolDestroyed()
2182 {
2183  QgsDebugMsg( QStringLiteral( "maptool destroyed" ) );
2184  mMapTool = nullptr;
2185 }
2186 
2187 bool QgsMapCanvas::event( QEvent *e )
2188 {
2189  if ( !QTouchDevice::devices().empty() )
2190  {
2191  if ( e->type() == QEvent::Gesture )
2192  {
2193  // call handler of current map tool
2194  if ( mMapTool )
2195  {
2196  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2197  }
2198  }
2199  }
2200 
2201  // pass other events to base class
2202  return QGraphicsView::event( e );
2203 }
2204 
2206 {
2207  // reload all layers in canvas
2208  const QList<QgsMapLayer *> layers = mapSettings().layers();
2209  for ( QgsMapLayer *layer : layers )
2210  {
2211  layer->reload();
2212  }
2213 
2214  redrawAllLayers();
2215 }
2216 
2218 {
2219  // clear the cache
2220  clearCache();
2221 
2222  // and then refresh
2223  refresh();
2224 }
2225 
2227 {
2228  while ( mRefreshScheduled || mJob )
2229  {
2230  QgsApplication::processEvents();
2231  }
2232 }
2233 
2235 {
2236  mSettings.setSegmentationTolerance( tolerance );
2237 }
2238 
2240 {
2241  mSettings.setSegmentationToleranceType( type );
2242 }
2243 
2244 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2245 {
2246  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2247  const QList<QGraphicsItem *> items = mScene->items();
2248  for ( QGraphicsItem *gi : items )
2249  {
2250  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( gi );
2251  if ( aItem )
2252  {
2253  annotationItemList.push_back( aItem );
2254  }
2255  }
2256 
2257  return annotationItemList;
2258 }
2259 
2261 {
2262  mAnnotationsVisible = show;
2263  const QList<QgsMapCanvasAnnotationItem *> items = annotationItems();
2264  for ( QgsMapCanvasAnnotationItem *item : items )
2265  {
2266  item->setVisible( show );
2267  }
2268 }
2269 
2271 {
2272  mSettings.setLabelingEngineSettings( settings );
2273 }
2274 
2276 {
2277  return mSettings.labelingEngineSettings();
2278 }
2279 
2280 void QgsMapCanvas::startPreviewJobs()
2281 {
2282  stopPreviewJobs(); //just in case still running
2283  schedulePreviewJob( 0 );
2284 }
2285 
2286 void QgsMapCanvas::startPreviewJob( int number )
2287 {
2288  QgsRectangle mapRect = mSettings.visibleExtent();
2289 
2290  if ( number == 4 )
2291  number += 1;
2292 
2293  int j = number / 3;
2294  int i = number % 3;
2295 
2296  //copy settings, only update extent
2297  QgsMapSettings jobSettings = mSettings;
2298 
2299  double dx = ( i - 1 ) * mapRect.width();
2300  double dy = ( 1 - j ) * mapRect.height();
2301  QgsRectangle jobExtent = mapRect;
2302 
2303  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
2304  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
2305  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
2306  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
2307 
2308  jobSettings.setExtent( jobExtent );
2309  jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
2310  jobSettings.setFlag( QgsMapSettings::RenderPreviewJob, true );
2311 
2312  // truncate preview layers to fast layers
2313  const QList<QgsMapLayer *> layers = jobSettings.layers();
2314  QList< QgsMapLayer * > previewLayers;
2316  context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
2317  for ( QgsMapLayer *layer : layers )
2318  {
2319  context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
2320  if ( !layer->dataProvider()->renderInPreview( context ) )
2321  {
2322  QgsDebugMsgLevel( QStringLiteral( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
2323  continue;
2324  }
2325 
2326  previewLayers << layer;
2327  }
2328  jobSettings.setLayers( previewLayers );
2329 
2330  QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
2331  job->setProperty( "number", number );
2332  mPreviewJobs.append( job );
2333  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2334  job->start();
2335 }
2336 
2337 void QgsMapCanvas::stopPreviewJobs()
2338 {
2339  mPreviewTimer.stop();
2340  const auto previewJobs = mPreviewJobs;
2341  for ( auto previewJob : previewJobs )
2342  {
2343  if ( previewJob )
2344  {
2345  disconnect( previewJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2346  connect( previewJob, &QgsMapRendererQImageJob::finished, previewJob, &QgsMapRendererQImageJob::deleteLater );
2347  previewJob->cancelWithoutBlocking();
2348  }
2349  }
2350  mPreviewJobs.clear();
2351 }
2352 
2353 void QgsMapCanvas::schedulePreviewJob( int number )
2354 {
2355  mPreviewTimer.setSingleShot( true );
2356  mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
2357  disconnect( mPreviewTimerConnection );
2358  mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
2359  {
2360  startPreviewJob( number );
2361  } );
2362  mPreviewTimer.start();
2363 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
bool previewJobsEnabled() const
Returns true if canvas map preview jobs (low priority render jobs which render portions of the view j...
The class is used as a container of context for various read/write operations on other objects...
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
Wrapper for iterator of features from vector data provider or vector layer.
void updateCanvasItemPositions()
called on resize or changed extent to notify canvas items to change their rectangle ...
int autoRefreshInterval
Definition: qgsmaplayer.h:84
void finished()
emitted when asynchronous rendering is finished (or canceled).
void setParallelRenderingEnabled(bool enabled)
Set whether the layers are rendered in parallel or sequentially.
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspointxy.h:124
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
QPoint mouseLastXY
Last seen point of the mouse.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns global labeling engine settings from the internal map settings.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:79
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
Job implementation that renders everything sequentially using a custom painter.
std::unique_ptr< CanvasProperties > mCanvasProperties
Handle pattern for implementation object.
Definition: qgsmapcanvas.h:903
virtual bool isEmpty() const
Returns true if the geometry is empty.
void setRotation(double degrees)
Set the rotation of the map canvas in clockwise degrees.
virtual void canvasMoveEvent(QgsMapMouseEvent *e)
Mouse move event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:157
void setRenderFlag(bool flag)
Sets whether a user has disabled canvas renders via the GUI.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds) ...
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
void zoomToNextExtent()
Zoom to the next extent (view)
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void zoomWithCenter(int x, int y, bool zoomIn)
Zooms in/out with a given center.
void setCanvasColor(const QColor &_newVal)
Write property of QColor bgColor.
void setCenter(const QgsPointXY &center)
Set the center of the map canvas, in geographical coordinates.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:151
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
QList< QgsMapLayer * > layers() const
Returns the list of layers shown within the map canvas.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads scope variables from an XML element.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
double magnificationFactor() const
Returns the magnification factor.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
void clearExtentHistory()
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
Maximum angle between generating radii (lines from arc center to output vertices) ...
bool event(QEvent *e) override
Overridden standard event to be gestures aware.
QColor selectionColor() const
Returns color for selected features.
bool mouseButtonDown
Flag to indicate status of mouse button.
void wheelEvent(QWheelEvent *e) override
Overridden mouse wheel event.
void canvasColorChanged()
Emitted when canvas background color changes.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void stopRendering()
stop rendering (if there is any right now)
double y
Definition: qgspointxy.h:48
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setPreviewJobsEnabled(bool enabled)
Sets whether canvas map preview jobs (low priority render jobs which render portions of the view just...
static QgsImageCache * imageCache()
Returns the application&#39;s image cache, used for caching resampled versions of raster images...
A class to represent a 2D point.
Definition: qgspointxy.h:43
double rotation() const
Gets the current map canvas rotation in clockwise degrees.
QgsPreviewEffect::PreviewMode previewMode() const
Returns the current preview mode for the map canvas.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:235
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
QColor backgroundColor() const
Gets the background color of the map.
void keyPressEvent(QKeyEvent *e) override
Overridden key press event.
void zoomToFeatureExtent(QgsRectangle &rect)
Zooms to feature extent.
Allow zooming by rectangle (by holding shift and dragging) while the tool is active.
Definition: qgsmaptool.h:94
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
void enableAntiAliasing(bool flag)
used to determine if anti-aliasing is enabled or not
An abstract class for items that can be placed on the map canvas.
void setCurrentLayer(QgsMapLayer *layer)
int layerCount() const
Returns number of layers on the map.
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
void moveCanvasContents(bool reset=false)
called when panning is in action, reset indicates end of panning
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setFlags(QgsMapSettings::Flags flags)
Sets combination of flags that will be used for rendering.
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
constexpr double CANVAS_MAGNIFICATION_MAX
Maximum magnification level allowed in map canvases.
Definition: qgsguiutils.h:69
void readProject(const QDomDocument &)
called to read map canvas settings from project
bool panSelectorDown
Flag to indicate the pan selector key is held down by user.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:121
void renderComplete(QPainter *)
Emitted when the canvas has rendered.
void projectColorsChanged()
Emitted whenever the project&#39;s color scheme has been changed.
Snapping utils instance that is connected to a canvas and updates the configuration (map settings + c...
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QgsMapTool * mapTool()
Returns the currently active tool.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
void mousePressEvent(QMouseEvent *e) override
Overridden mouse press event.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:102
void mapThemesChanged()
Emitted when map themes within the collection are changed.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
virtual QImage renderedImage()=0
Gets a preview/resulting image.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:73
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
QgsExpressionContextScope * defaultExpressionContextScope()
Creates a new scope which contains default variables and functions relating to the map canvas...
void selectionChanged(QgsVectorLayer *layer)
Emitted when selection in any layer gets changed.
Enable drawing of labels on top of the map.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
double maxRenderingTimeMs
Default maximum allowable render time, in ms.
void zoomLastStatusChanged(bool)
Emitted when zoom last status changed.
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
virtual void canvasPressEvent(QgsMapMouseEvent *e)
Mouse press event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:167
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
void magnificationChanged(double)
Emitted when the scale of the map changes.
QString what() const
Definition: qgsexception.h:48
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void updateScale()
Emits signal scaleChanged to update scale in main window.
QgsUnitTypes::DistanceUnit mapUnits() const
Gets units of map&#39;s geographical coordinates - used for scale calculation.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
void resizeEvent(QResizeEvent *e) override
Overridden resize event.
Deprecated to be deleted, stuff from here should be moved elsewhere.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:175
void enableMapTileRendering(bool flag)
sets map tile rendering flag
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:83
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
void setSnappingUtils(QgsSnappingUtils *utils)
Assign an instance of snapping utils to the map canvas.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
virtual void keyReleaseEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:187
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
QgsRectangle boundingBoxOfSelected() const
Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,0,0,0) is returned.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
Map tool is an edit tool, which can only be used when layer is editable.
Definition: qgsmaptool.h:93
void setOutputSize(QSize size)
Sets the size of the resulting map image.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
void start() override
Start the rendering job and immediately return.
void saveAsImage(const QString &fileName, QPixmap *QPixmap=nullptr, const QString &="PNG")
Save the contents of the map canvas to disk as an image.
QgsMapThemeCollection mapThemeCollection
Definition: qgsproject.h:99
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global labeling engine settings in the internal map settings.
void mapCanvasRefreshed()
Emitted when canvas finished a refresh request.
void rotationChanged(double)
Emitted when the rotation of the map changes.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setMagnificationFactor(double factor)
Set the magnification factor.
void zoomNextStatusChanged(bool)
Emitted when zoom next status changed.
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
A circle is used to highlight points (○)
Definition: qgsrubberband.h:79
void flashFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of features with matching ids from a vector layer to flash within the canvas...
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled) ...
void reload() FINAL
Synchronises with changes in the datasource.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsMapCanvas(QWidget *parent=nullptr)
Constructor.
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:41
double scale() const
Returns the calculated map scale.
Job implementation that renders all layers in parallel.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
void setMapUpdateInterval(int timeMilliseconds)
Set how often map preview should be updated while it is being rendered (in milliseconds) ...
void keyReleased(QKeyEvent *e)
Emit key release event.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
virtual void waitForFinished()=0
Block until the job has finished.
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
void setDevicePixelRatio(float dpr)
Sets the device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" d...
void readProject(const QDomDocument &)
Emitted when a project is being read.
Enable anti-aliasing for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:812
void mouseDoubleClickEvent(QMouseEvent *e) override
Overridden mouse double-click event.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void panToSelected(QgsVectorLayer *layer=nullptr)
Pan to the selected features of current (vector) layer keeping same extent.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
void setWheelFactor(double factor)
Sets wheel zoom factor (should be greater than 1)
void destinationCrsChanged()
Emitted when map CRS has changed.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
bool isFrozen() const
Returns true if canvas is frozen.
void setCachingEnabled(bool enabled)
Set whether to cache images of rendered layers.
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:99
Single scope for storing variables and functions for use within a QgsExpressionContext.
double mapUnitsPerPixel() const
Returns current map units per pixel.
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets a preview mode for the map canvas.
virtual void wheelEvent(QWheelEvent *e)
Mouse wheel event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:177
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
void transformContextChanged()
Emitted when the canvas transform context is changed.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes scope variables to an XML element.
void setMode(PreviewMode mode)
Sets the mode for the preview effect, which controls how the effect modifies a widgets appearance...
void renderStarting()
Emitted when the canvas is about to be rendered.
float devicePixelRatio() const
Returns device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" di...
const QgsMapToPixel & mapToPixel() const
void keyPressed(QKeyEvent *e)
Emit key press event.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
void zoomOut()
Zoom out with fixed factor.
Enable drawing of vertex markers for layers in editing mode.
void waitWhileRendering()
Blocks until the rendering job has finished.
constexpr double CANVAS_MAGNIFICATION_MIN
Minimum magnification level allowed in map canvases.
Definition: qgsguiutils.h:61
void zoomToPreviousExtent()
Zoom to the previous extent (view)
bool isDrawing()
Find out whether rendering is in progress.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
double x
Definition: qgspointxy.h:47
virtual bool renderInPreview(const QgsDataProvider::PreviewContext &context)
Returns whether the layer must be rendered in preview jobs.
void zoomToSelected(QgsVectorLayer *layer=nullptr)
Zoom to the extent of the selected features of provided (vector) layer.
A class to represent a vector.
Definition: qgsvector.h:29
PreviewMode mode() const
Returns the mode used for the preview effect.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
virtual void start()=0
Start the rendering job and immediately return.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QPoint mouseLastXY()
returns last position of mouse cursor
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
double magnificationFactor() const
Returns the magnification factor.
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QMap< QString, QString > layerStyleOverrides() const
Returns the stored overrides of styles for layers.
void mouseMoveEvent(QMouseEvent *e) override
Overridden mouse move event.
void keyReleaseEvent(QKeyEvent *e) override
Overridden key release event.
virtual void keyPressEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:182
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:54
void flashGeometries(const QList< QgsGeometry > &geometries, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of geometries to flash within the canvas.
double scale() const
Returns the last reported scale of the canvas.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
QColor canvasColor() const
Read property of QColor bgColor.
void clear()
Invalidates the cache contents, clearing all cached images.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
Abstract base class for all map tools.
Definition: qgsmaptool.h:62
void selectionChangedSlot()
Receives signal about selection change, and pass it on with layer info.
Draw map such that there are no problems between adjacent tiles.
Job implementation that renders everything sequentially in one thread.
Render is a &#39;canvas preview&#39; render, and shortcuts should be taken to ensure fast rendering...
QgsUnitTypes::DistanceUnit mapUnits() const
Convenience function for returning the current canvas map units.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void mouseReleaseEvent(QMouseEvent *e) override
Overridden mouse release event.
void writeProject(QDomDocument &)
Emitted when the project is being written.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:212
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
static QgsExpressionContextScope * atlasScope(QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QString theme() const
Returns the map&#39;s theme shown in the canvas, if set.
Definition: qgsmapcanvas.h:425
QgsPointXY center() const
Gets map center, in geographical coordinates.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
virtual bool gestureEvent(QGestureEvent *e)
gesture event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:192
void setSelectionColor(const QColor &color)
Sets color that is used for drawing of selected vector features.
void layerStyleOverridesChanged()
Emitted when the configuration of overridden layer styles changes.
virtual void canvasDoubleClickEvent(QgsMapMouseEvent *e)
Mouse double-click event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:162
void dragEnterEvent(QDragEnterEvent *e) override
Overridden drag enter event.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer&#39;s CRS to output CRS
void writeProject(QDomDocument &)
called to write map canvas settings to project
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
void setLayers(const QList< QgsMapLayer *> &layers)
Sets the list of layers that should be shown in the canvas.
QgsRectangle fullExtent() const
returns current extent of layer set
Intermediate base class adding functionality that allows client to query the rendered image...
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter=true)
Centers canvas extent to feature ids.
Stores global configuration for labeling engine.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:441
void zoomToFullExtent()
Zoom to the full extent of all layers.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
This class has all the configuration of snapping and can return answers to snapping queries...
const QgsLabelingResults * labelingResults() const
Gets access to the labeling results (may be nullptr)
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
void setMapSettingsFlags(QgsMapSettings::Flags flags)
Resets the flags for the canvas&#39; map settings.
void refreshAllLayers()
Reload all layers (including refreshing layer properties from their data sources), clears the cache and refreshes the canvas.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
void zoomScale(double scale)
Zooms the canvas to a specific scale.
Class for doing transforms between two map coordinate systems.
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
void renderErrorOccurred(const QString &error, QgsMapLayer *layer)
Emitted whenever an error is encountered during a map render operation.
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
Definition: qgssettings.h:244
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
virtual void cancelWithoutBlocking()=0
Triggers cancellation of the rendering job without blocking.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
void setSelectionColor(const QColor &color)
Set color of selected vector features.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
void paintEvent(QPaintEvent *e) override
Overridden paint event.
void layerStateChange()
This slot is connected to the visibility change of one or more layers.
double lastRenderingTimeMs
Previous rendering time for the layer, in ms.
Enable vector simplification and other rendering optimizations.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
void freeze(bool frozen=true)
Freeze/thaw the map canvas.
void setScaleLocked(bool isLocked)
Lock the scale, so zooming can be performed using magnication.
Class that stores computed placement from labeling engine.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
This class is responsible for keeping cache of rendered images resulting from a map rendering job...
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QgsGeometry geometry
Definition: qgsfeature.h:67
void transformContextChanged()
Emitted when the project transformContext() is changed.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
QColor selectionColor() const
Gets color that is used for drawing of selected vector features.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
void readXml(QDomNode &node)
bool nextFeature(QgsFeature &f)
QPoint rubberStartPoint
Beginning point of a rubber band.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
virtual QgsLabelingResults * takeLabelingResults()=0
Gets pointer to internal labeling engine (in order to get access to the results). ...
void xyCoordinates(const QgsPointXY &p)
Emits current mouse position.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void writeXml(QDomNode &node, QDomDocument &doc)
void zoomIn()
Zoom in with fixed factor.
Stores settings related to the context in which a preview job runs.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
virtual void updatePosition()
called on changed extent or resize event to update position of the item
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
CanvasProperties()=default
Constructor for CanvasProperties.
An interactive map canvas item which displays a QgsAnnotation.
void themeChanged(const QString &theme)
Emitted when the canvas has been assigned a different map theme.
void extentsChanged()
Emitted when the extents of the map change.
virtual void clean()
convenient method to clean members
Definition: qgsmaptool.cpp:109
QSize outputSize() const
Returns the size of the resulting map image.
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
QgsMapLayer * layer(int index)
Returns the map layer at position index in the layer stack.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
virtual bool isActive() const =0
Tell whether the rendering job is currently running in background.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
void setMagnificationFactor(double factor)
Sets the factor of magnification to apply to the map canvas.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
void panActionEnd(QPoint releasePoint)
Ends pan action and redraws the canvas.
void redrawAllLayers()
Clears all cached images and redraws all layers.
bool previewModeEnabled() const
Returns whether a preview mode is enabled for the map canvas.
void layersChanged()
Emitted when a new set of layers has been received.
void messageEmitted(const QString &title, const QString &message, Qgis::MessageLevel=Qgis::Info)
emit a message (usually to be displayed in a message bar)
virtual bool usedCachedLabels() const =0
Returns true if the render job was able to use a cached labeling solution.
~QgsMapCanvas() override
virtual void canvasReleaseEvent(QgsMapMouseEvent *e)
Mouse release event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:172