QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
78 //TODO QGIS 3.0 - remove
80 {
81  public:
82 
86  CanvasProperties() = default;
87 
89  bool mouseButtonDown{ false };
90 
92  QPoint mouseLastXY;
93 
96 
98  bool panSelectorDown{ false };
99 };
100 
101 
102 
103 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
104  : QGraphicsView( parent )
106  , mExpressionContextScope( tr( "Map Canvas" ) )
107 {
108  mScene = new QGraphicsScene();
109  setScene( mScene );
110  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
111  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
112  setMouseTracking( true );
113  setFocusPolicy( Qt::StrongFocus );
114 
115  mResizeTimer = new QTimer( this );
116  mResizeTimer->setSingleShot( true );
117  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
118 
119  mRefreshTimer = new QTimer( this );
120  mRefreshTimer->setSingleShot( true );
121  connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
122 
123  // create map canvas item which will show the map
124  mMap = new QgsMapCanvasMap( this );
125 
126  // project handling
128  this, &QgsMapCanvas::readProject );
131 
132  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
133  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
134 
138  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
140  this, [ = ]
141  {
142  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
143  refresh();
144  } );
145  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
147  this, [ = ]
148  {
149  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
151  refresh();
152  } );
153 
154  // refresh canvas when a remote svg has finished downloading
156 
157  //segmentation parameters
158  QgsSettings settings;
159  double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
160  QgsAbstractGeometry::SegmentationToleranceType toleranceType = settings.enumValue( QStringLiteral( "qgis/segmentationToleranceType" ), QgsAbstractGeometry::MaximumAngle );
161  mSettings.setSegmentationTolerance( segmentationTolerance );
162  mSettings.setSegmentationToleranceType( toleranceType );
163 
164  mWheelZoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
165 
166  QSize s = viewport()->size();
167  mSettings.setOutputSize( s );
168  mSettings.setDevicePixelRatio( devicePixelRatio() );
169  setSceneRect( 0, 0, s.width(), s.height() );
170  mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
171 
172  moveCanvasContents( true );
173 
174  // keep device pixel ratio up to date on screen or resolution change
175  if ( window()->windowHandle() )
176  {
177  connect( window()->windowHandle(), &QWindow::screenChanged, this, [ = ]( QScreen * ) {mSettings.setDevicePixelRatio( devicePixelRatio() );} );
178  connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, [ = ]( qreal ) {mSettings.setDevicePixelRatio( devicePixelRatio() );} );
179  }
180 
181  connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsMapCanvas::mapUpdateTimeout );
182  mMapUpdateTimer.setInterval( 250 );
183 
184 #ifdef Q_OS_WIN
185  // Enable touch event on Windows.
186  // Qt on Windows needs to be told it can take touch events or else it ignores them.
187  grabGesture( Qt::PinchGesture );
188  viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
189 #endif
190 
191  mPreviewEffect = new QgsPreviewEffect( this );
192  viewport()->setGraphicsEffect( mPreviewEffect );
193 
194  mZoomCursor = QgsApplication::getThemeCursor( QgsApplication::Cursor::ZoomIn );
195 
196  connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
197 
199 
200  setInteractive( false );
201 
202  // make sure we have the same default in QgsMapSettings and the scene's background brush
203  // (by default map settings has white bg color, scene background brush is black)
204  setCanvasColor( mSettings.backgroundColor() );
205 
206  refresh();
207 
208 } // QgsMapCanvas ctor
209 
210 
212 {
213  if ( mMapTool )
214  {
215  mMapTool->deactivate();
216  mMapTool = nullptr;
217  }
218  mLastNonZoomMapTool = nullptr;
219 
220  // rendering job may still end up writing into canvas map item
221  // so kill it before deleting canvas items
222  if ( mJob )
223  {
224  whileBlocking( mJob )->cancel();
225  delete mJob;
226  }
227 
228  QList< QgsMapRendererQImageJob * >::const_iterator previewJob = mPreviewJobs.constBegin();
229  for ( ; previewJob != mPreviewJobs.constEnd(); ++previewJob )
230  {
231  if ( *previewJob )
232  {
233  whileBlocking( *previewJob )->cancel();
234  delete *previewJob;
235  }
236  }
237 
238  // delete canvas items prior to deleting the canvas
239  // because they might try to update canvas when it's
240  // already being destructed, ends with segfault
241  QList<QGraphicsItem *> list = mScene->items();
242  QList<QGraphicsItem *>::iterator it = list.begin();
243  while ( it != list.end() )
244  {
245  QGraphicsItem *item = *it;
246  delete item;
247  ++it;
248  }
249 
250  mScene->deleteLater(); // crashes in python tests on windows
251 
252  delete mCache;
253  delete mLabelingResults;
254 }
255 
257 {
258  // do not go higher or lower than min max magnification ratio
259  double magnifierMin = QgsGuiUtils::CANVAS_MAGNIFICATION_MIN;
260  double magnifierMax = QgsGuiUtils::CANVAS_MAGNIFICATION_MAX;
261  factor = qBound( magnifierMin, factor, magnifierMax );
262 
263  // the magnifier widget is in integer percent
264  if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
265  {
266  mSettings.setMagnificationFactor( factor );
267  refresh();
268  emit magnificationChanged( factor );
269  }
270 }
271 
273 {
274  return mSettings.magnificationFactor();
275 }
276 
278 {
279  mSettings.setFlag( QgsMapSettings::Antialiasing, flag );
280 } // anti aliasing
281 
283 {
284  mSettings.setFlag( QgsMapSettings::RenderMapTile, flag );
285 }
286 
288 {
289  QList<QgsMapLayer *> layers = mapSettings().layers();
290  if ( index >= 0 && index < layers.size() )
291  return layers[index];
292  else
293  return nullptr;
294 }
295 
297 {
298  if ( mCurrentLayer == layer )
299  return;
300 
301  mCurrentLayer = layer;
302  emit currentLayerChanged( layer );
303 }
304 
305 double QgsMapCanvas::scale() const
306 {
307  return mapSettings().scale();
308 }
309 
311 {
312  return nullptr != mJob;
313 } // isDrawing
314 
315 // return the current coordinate transform based on the extents and
316 // device size
318 {
319  return &mapSettings().mapToPixel();
320 }
321 
322 void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
323 {
324  // following a theme => request denied!
325  if ( !mTheme.isEmpty() )
326  return;
327 
328  setLayersPrivate( layers );
329 }
330 
331 void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
332 {
333  QList<QgsMapLayer *> oldLayers = mSettings.layers();
334 
335  // update only if needed
336  if ( layers == oldLayers )
337  return;
338 
339  Q_FOREACH ( QgsMapLayer *layer, oldLayers )
340  {
341  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
342  disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
343  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
344  {
346  }
347  }
348 
349  mSettings.setLayers( layers );
350 
351  Q_FOREACH ( QgsMapLayer *layer, layers )
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 
596 void QgsMapCanvas::rendererJobFinished()
597 {
598  QgsDebugMsg( QStringLiteral( "CANVAS finish! %1" ).arg( !mJobCanceled ) );
599 
600  mMapUpdateTimer.stop();
601 
602  // TODO: would be better to show the errors in message bar
603  Q_FOREACH ( const QgsMapRendererJob::Error &error, mJob->errors() )
604  {
605  QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) );
606  }
607 
608  if ( !mJobCanceled )
609  {
610  // take labeling results before emitting renderComplete, so labeling map tools
611  // connected to signal work with correct results
612  if ( !mJob->usedCachedLabels() )
613  {
614  delete mLabelingResults;
615  mLabelingResults = mJob->takeLabelingResults();
616  }
617 
618  QImage img = mJob->renderedImage();
619 
620  // emit renderComplete to get our decorations drawn
621  QPainter p( &img );
622  emit renderComplete( &p );
623 
624  QgsSettings settings;
625  if ( settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
626  {
627  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
628  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
629  }
630 
631  if ( mDrawRenderingStats )
632  {
633  int w = img.width(), h = img.height();
634  QFont fnt = p.font();
635  fnt.setBold( true );
636  p.setFont( fnt );
637  int lh = p.fontMetrics().height() * 2;
638  QRect r( 0, h - lh, w, lh );
639  p.setPen( Qt::NoPen );
640  p.setBrush( QColor( 0, 0, 0, 110 ) );
641  p.drawRect( r );
642  p.setPen( Qt::white );
643  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? QStringLiteral( "PARALLEL" ) : QStringLiteral( "SEQUENTIAL" ) ).arg( mJob->renderingTime() );
644  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
645  }
646 
647  p.end();
648 
649  mMap->setContent( img, imageRect( img, mSettings ) );
650 
651  mLastLayerRenderTime.clear();
652  const auto times = mJob->perLayerRenderingTime();
653  for ( auto it = times.constBegin(); it != times.constEnd(); ++it )
654  {
655  mLastLayerRenderTime.insert( it.key()->id(), it.value() );
656  }
657  if ( mUsePreviewJobs )
658  startPreviewJobs();
659  }
660 
661  // now we are in a slot called from mJob - do not delete it immediately
662  // so the class is still valid when the execution returns to the class
663  mJob->deleteLater();
664  mJob = nullptr;
665 
666  emit mapCanvasRefreshed();
667 }
668 
669 void QgsMapCanvas::previewJobFinished()
670 {
671  QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
672  Q_ASSERT( job );
673 
674  if ( mMap )
675  {
676  mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
677  mPreviewJobs.removeAll( job );
678 
679  int number = job->property( "number" ).toInt();
680  if ( number < 8 )
681  {
682  startPreviewJob( number + 1 );
683  }
684 
685  delete job;
686  }
687 }
688 
689 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
690 {
691  // This is a hack to pass QgsMapCanvasItem::setRect what it
692  // expects (encoding of position and size of the item)
693  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
694  QgsPointXY topLeft = m2p.toMapCoordinates( 0, 0 );
695 #ifdef QGISDEBUG
696  // do not assert this, since it might lead to crashes when changing screen while rendering
697  if ( img.devicePixelRatio() != mapSettings.devicePixelRatio() )
698  {
699  QgsLogger::warning( QStringLiteral( "The renderer map has a wrong device pixel ratio" ) );
700  }
701 #endif
702 #if QT_VERSION >= 0x050600
703  double res = m2p.mapUnitsPerPixel() / img.devicePixelRatioF();
704 #else
705  double res = m2p.mapUnitsPerPixel() / img.devicePixelRatio();
706 #endif
707  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
708  return rect;
709 }
710 
712 {
713  return mUsePreviewJobs;
714 }
715 
717 {
718  mUsePreviewJobs = enabled;
719 }
720 
721 void QgsMapCanvas::mapUpdateTimeout()
722 {
723  if ( mJob )
724  {
725  const QImage &img = mJob->renderedImage();
726  mMap->setContent( img, imageRect( img, mSettings ) );
727  }
728 }
729 
731 {
732  if ( mJob )
733  {
734  QgsDebugMsg( QStringLiteral( "CANVAS stop rendering!" ) );
735  mJobCanceled = true;
736  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
737  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
738  mJob->cancelWithoutBlocking();
739  mJob = nullptr;
740  }
741  stopPreviewJobs();
742 }
743 
744 //the format defaults to "PNG" if not specified
745 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
746 {
747  QPainter painter;
748  QImage image;
749 
750  //
751  //check if the optional QPaintDevice was supplied
752  //
753  if ( theQPixmap )
754  {
755  image = theQPixmap->toImage();
756  painter.begin( &image );
757 
758  // render
759  QgsMapRendererCustomPainterJob job( mSettings, &painter );
760  job.start();
761  job.waitForFinished();
762  emit renderComplete( &painter );
763  }
764  else //use the map view
765  {
766  image = mMap->contentImage().copy();
767  painter.begin( &image );
768  }
769 
770  // draw annotations
771  QStyleOptionGraphicsItem option;
772  option.initFrom( this );
773  QGraphicsItem *item = nullptr;
774  QListIterator<QGraphicsItem *> i( items() );
775  i.toBack();
776  while ( i.hasPrevious() )
777  {
778  item = i.previous();
779 
780  if ( !( item && dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) ) )
781  {
782  continue;
783  }
784 
785  painter.save();
786 
787  QPointF itemScenePos = item->scenePos();
788  painter.translate( itemScenePos.x(), itemScenePos.y() );
789 
790  item->paint( &painter, &option );
791 
792  painter.restore();
793  }
794 
795  painter.end();
796  image.save( fileName, format.toLocal8Bit().data() );
797 
798  QFileInfo myInfo = QFileInfo( fileName );
799 
800  // build the world file name
801  QString outputSuffix = myInfo.suffix();
802  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
803  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
804  QFile myWorldFile( myWorldFileName );
805  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
806  {
807  return;
808  }
809  QTextStream myStream( &myWorldFile );
811 } // saveAsImage
812 
813 
814 
816 {
817  return mapSettings().visibleExtent();
818 } // extent
819 
821 {
822  return mapSettings().fullExtent();
823 } // extent
824 
825 
826 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
827 {
828  QgsRectangle current = extent();
829 
830  if ( ( r == current ) && magnified )
831  return;
832 
833  if ( r.isEmpty() )
834  {
835  if ( !mSettings.hasValidSettings() )
836  {
837  // we can't even just move the map center
838  QgsDebugMsg( QStringLiteral( "Empty extent - ignoring" ) );
839  return;
840  }
841 
842  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
843  QgsDebugMsg( QStringLiteral( "Empty extent - keeping old scale with new center!" ) );
844  setCenter( r.center() );
845  }
846  else
847  {
848  mSettings.setExtent( r, magnified );
849  }
850  emit extentsChanged();
851  updateScale();
852  if ( mLastExtent.size() > 20 )
853  mLastExtent.removeAt( 0 );
854 
855  //clear all extent items after current index
856  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
857  {
858  mLastExtent.removeAt( i );
859  }
860 
861  mLastExtent.append( extent() );
862 
863  // adjust history to no more than 20
864  if ( mLastExtent.size() > 20 )
865  {
866  mLastExtent.removeAt( 0 );
867  }
868 
869  // the last item is the current extent
870  mLastExtentIndex = mLastExtent.size() - 1;
871 
872  // update controls' enabled state
873  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
874  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
875 } // setExtent
876 
878 {
880  double x = center.x();
881  double y = center.y();
882  setExtent(
883  QgsRectangle(
884  x - r.width() / 2.0, y - r.height() / 2.0,
885  x + r.width() / 2.0, y + r.height() / 2.0
886  ),
887  true
888  );
889 } // setCenter
890 
892 {
894  return r.center();
895 }
896 
897 QgsPointXY QgsMapCanvas::cursorPoint() const
898 {
899  return mCursorPoint;
900 }
901 
903 {
904  return mapSettings().rotation();
905 } // rotation
906 
907 void QgsMapCanvas::setRotation( double degrees )
908 {
909  double current = rotation();
910 
911  if ( qgsDoubleNear( degrees, current ) )
912  return;
913 
914  mSettings.setRotation( degrees );
915  emit rotationChanged( degrees );
916  emit extentsChanged(); // visible extent changes with rotation
917 } // setRotation
918 
919 
921 {
922  emit scaleChanged( mapSettings().scale() );
923 }
924 
925 
927 {
929  // If the full extent is an empty set, don't do the zoom
930  if ( !extent.isEmpty() )
931  {
932  // Add a 5% margin around the full extent
933  extent.scale( 1.05 );
934  setExtent( extent );
935  }
936  refresh();
937 
938 } // zoomToFullExtent
939 
940 
942 {
943  if ( mLastExtentIndex > 0 )
944  {
945  mLastExtentIndex--;
946  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
947  emit extentsChanged();
948  updateScale();
949  refresh();
950  // update controls' enabled state
951  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
952  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
953  }
954 
955 } // zoomToPreviousExtent
956 
958 {
959  if ( mLastExtentIndex < mLastExtent.size() - 1 )
960  {
961  mLastExtentIndex++;
962  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
963  emit extentsChanged();
964  updateScale();
965  refresh();
966  // update controls' enabled state
967  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
968  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
969  }
970 }// zoomToNextExtent
971 
973 {
974  mLastExtent.clear(); // clear the zoom history list
975  mLastExtent.append( extent() ) ; // set the current extent in the list
976  mLastExtentIndex = mLastExtent.size() - 1;
977  // update controls' enabled state
978  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
979  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
980 }// clearExtentHistory
981 
983 {
984  if ( !layer )
985  {
986  // use current layer by default
987  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
988  }
989 
990  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
991  return;
992 
993  QgsRectangle rect = layer->boundingBoxOfSelected();
994  if ( rect.isNull() )
995  {
996  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
997  return;
998  }
999 
1000  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1001 
1002  // zoom in if point cannot be distinguished from others
1003  // also check that rect is empty, as it might not in case of multi points
1004  if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1005  {
1006  int scaleFactor = 5;
1007  QgsPointXY centerMapCoordinates = rect.center();
1008  QgsPointXY centerLayerCoordinates = mSettings.mapToLayerCoordinates( layer, centerMapCoordinates );
1009  QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, &centerLayerCoordinates );
1011  QgsFeatureIterator fit = layer->getFeatures( req );
1012  QgsFeature f;
1013  QgsPointXY closestPoint;
1014  double closestSquaredDistance = pow( extentRect.width(), 2.0 ) + pow( extentRect.height(), 2.0 );
1015  bool pointFound = false;
1016  while ( fit.nextFeature( f ) )
1017  {
1018  QgsPointXY point = f.geometry().asPoint();
1019  double sqrDist = point.sqrDist( centerLayerCoordinates );
1020  if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits<double>::epsilon() )
1021  continue;
1022  pointFound = true;
1023  closestPoint = point;
1024  closestSquaredDistance = sqrDist;
1025  }
1026  if ( pointFound )
1027  {
1028  // combine selected point with closest point and scale this rect
1029  rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) );
1030  rect.scale( scaleFactor, &centerMapCoordinates );
1031  }
1032  }
1033 
1034  zoomToFeatureExtent( rect );
1035 }
1036 
1038 {
1039  // no selected features, only one selected point feature
1040  //or two point features with the same x- or y-coordinates
1041  if ( rect.isEmpty() )
1042  {
1043  // zoom in
1044  QgsPointXY c = rect.center();
1045  rect = extent();
1046  rect.scale( 1.0, &c );
1047  }
1048  //zoom to an area
1049  else
1050  {
1051  // Expand rect to give a bit of space around the selected
1052  // objects so as to keep them clear of the map boundaries
1053  // The same 5% should apply to all margins.
1054  rect.scale( 1.05 );
1055  }
1056 
1057  setExtent( rect );
1058  refresh();
1059 }
1060 
1062 {
1063  if ( !layer )
1064  {
1065  return;
1066  }
1067 
1068  QgsRectangle bbox;
1069  QString errorMsg;
1070  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1071  {
1072  zoomToFeatureExtent( bbox );
1073  }
1074  else
1075  {
1076  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, Qgis::Warning );
1077  }
1078 
1079 }
1080 
1082 {
1083  if ( !layer )
1084  {
1085  return;
1086  }
1087 
1088  QgsRectangle bbox;
1089  QString errorMsg;
1090  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1091  {
1092  setCenter( bbox.center() );
1093  refresh();
1094  }
1095  else
1096  {
1097  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, Qgis::Warning );
1098  }
1099 }
1100 
1101 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1102 {
1103  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1104  bbox.setMinimal();
1105  QgsFeature fet;
1106  int featureCount = 0;
1107  errorMsg.clear();
1108 
1109  while ( it.nextFeature( fet ) )
1110  {
1111  QgsGeometry geom = fet.geometry();
1112  if ( geom.isNull() )
1113  {
1114  errorMsg = tr( "Feature does not have a geometry" );
1115  }
1116  else if ( geom.constGet()->isEmpty() )
1117  {
1118  errorMsg = tr( "Feature geometry is empty" );
1119  }
1120  if ( !errorMsg.isEmpty() )
1121  {
1122  return false;
1123  }
1125  bbox.combineExtentWith( r );
1126  featureCount++;
1127  }
1128 
1129  if ( featureCount != ids.count() )
1130  {
1131  errorMsg = tr( "Feature not found" );
1132  return false;
1133  }
1134 
1135  return true;
1136 }
1137 
1139 {
1140  if ( !layer )
1141  {
1142  // use current layer by default
1143  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1144  }
1145 
1146  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1147  return;
1148 
1149  QgsRectangle rect = layer->boundingBoxOfSelected();
1150  if ( rect.isNull() )
1151  {
1152  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
1153  return;
1154  }
1155 
1156  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1157  setCenter( rect.center() );
1158  refresh();
1159 }
1160 
1162  const QColor &color1, const QColor &color2,
1163  int flashes, int duration )
1164 {
1165  if ( !layer )
1166  {
1167  return;
1168  }
1169 
1170  QList< QgsGeometry > geoms;
1171 
1172  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1173  QgsFeature fet;
1174  while ( it.nextFeature( fet ) )
1175  {
1176  if ( !fet.hasGeometry() )
1177  continue;
1178  geoms << fet.geometry();
1179  }
1180 
1181  flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
1182 }
1183 
1184 void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
1185 {
1186  if ( geometries.isEmpty() )
1187  return;
1188 
1189  QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
1190  QgsRubberBand *rb = new QgsRubberBand( this, geomType );
1191  for ( const QgsGeometry &geom : geometries )
1192  rb->addGeometry( geom, crs );
1193 
1194  if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
1195  {
1196  rb->setWidth( 2 );
1197  rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
1198  }
1199  if ( geomType == QgsWkbTypes::PointGeometry )
1200  rb->setIcon( QgsRubberBand::ICON_CIRCLE );
1201 
1202  QColor startColor = color1;
1203  if ( !startColor.isValid() )
1204  {
1205  if ( geomType == QgsWkbTypes::PolygonGeometry )
1206  {
1207  startColor = rb->fillColor();
1208  }
1209  else
1210  {
1211  startColor = rb->strokeColor();
1212  }
1213  startColor.setAlpha( 255 );
1214  }
1215  QColor endColor = color2;
1216  if ( !endColor.isValid() )
1217  {
1218  endColor = startColor;
1219  endColor.setAlpha( 0 );
1220  }
1221 
1222 
1223  QVariantAnimation *animation = new QVariantAnimation( this );
1224  connect( animation, &QVariantAnimation::finished, this, [animation, rb]
1225  {
1226  animation->deleteLater();
1227  delete rb;
1228  } );
1229  connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
1230  {
1231  QColor c = value.value<QColor>();
1232  if ( geomType == QgsWkbTypes::PolygonGeometry )
1233  {
1234  rb->setFillColor( c );
1235  }
1236  else
1237  {
1238  rb->setStrokeColor( c );
1239  QColor c = rb->secondaryStrokeColor();
1240  c.setAlpha( c.alpha() );
1241  rb->setSecondaryStrokeColor( c );
1242  }
1243  rb->update();
1244  } );
1245 
1246  animation->setDuration( duration * flashes );
1247  animation->setStartValue( endColor );
1248  double midStep = 0.2 / flashes;
1249  for ( int i = 0; i < flashes; ++i )
1250  {
1251  double start = static_cast< double >( i ) / flashes;
1252  animation->setKeyValueAt( start + midStep, startColor );
1253  double end = static_cast< double >( i + 1 ) / flashes;
1254  if ( !qgsDoubleNear( end, 1.0 ) )
1255  animation->setKeyValueAt( end, endColor );
1256  }
1257  animation->setEndValue( endColor );
1258  animation->start();
1259 }
1260 
1261 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1262 {
1263  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1264  {
1265  emit keyPressed( e );
1266  return;
1267  }
1268 
1269  if ( ! mCanvasProperties->mouseButtonDown )
1270  {
1271  // Don't want to interfer with mouse events
1272 
1273  QgsRectangle currentExtent = mapSettings().visibleExtent();
1274  double dx = std::fabs( currentExtent.width() / 4 );
1275  double dy = std::fabs( currentExtent.height() / 4 );
1276 
1277  switch ( e->key() )
1278  {
1279  case Qt::Key_Left:
1280  QgsDebugMsg( QStringLiteral( "Pan left" ) );
1281  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1282  refresh();
1283  break;
1284 
1285  case Qt::Key_Right:
1286  QgsDebugMsg( QStringLiteral( "Pan right" ) );
1287  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1288  refresh();
1289  break;
1290 
1291  case Qt::Key_Up:
1292  QgsDebugMsg( QStringLiteral( "Pan up" ) );
1293  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1294  refresh();
1295  break;
1296 
1297  case Qt::Key_Down:
1298  QgsDebugMsg( QStringLiteral( "Pan down" ) );
1299  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1300  refresh();
1301  break;
1302 
1303 
1304 
1305  case Qt::Key_Space:
1306  QgsDebugMsg( QStringLiteral( "Pressing pan selector" ) );
1307 
1308  //mCanvasProperties->dragging = true;
1309  if ( ! e->isAutoRepeat() )
1310  {
1311  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1312  mCanvasProperties->panSelectorDown = true;
1313  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1314  }
1315  break;
1316 
1317  case Qt::Key_PageUp:
1318  QgsDebugMsg( QStringLiteral( "Zoom in" ) );
1319  zoomIn();
1320  break;
1321 
1322  case Qt::Key_PageDown:
1323  QgsDebugMsg( QStringLiteral( "Zoom out" ) );
1324  zoomOut();
1325  break;
1326 
1327 #if 0
1328  case Qt::Key_P:
1329  mUseParallelRendering = !mUseParallelRendering;
1330  refresh();
1331  break;
1332 
1333  case Qt::Key_S:
1334  mDrawRenderingStats = !mDrawRenderingStats;
1335  refresh();
1336  break;
1337 #endif
1338 
1339  default:
1340  // Pass it on
1341  if ( mMapTool )
1342  {
1343  mMapTool->keyPressEvent( e );
1344  }
1345  else e->ignore();
1346 
1347  QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
1348  }
1349  }
1350 
1351  emit keyPressed( e );
1352 
1353 } //keyPressEvent()
1354 
1355 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1356 {
1357  QgsDebugMsg( QStringLiteral( "keyRelease event" ) );
1358 
1359  switch ( e->key() )
1360  {
1361  case Qt::Key_Space:
1362  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1363  {
1364  QgsDebugMsg( QStringLiteral( "Releasing pan selector" ) );
1365  QApplication::restoreOverrideCursor();
1366  mCanvasProperties->panSelectorDown = false;
1367  panActionEnd( mCanvasProperties->mouseLastXY );
1368  }
1369  break;
1370 
1371  default:
1372  // Pass it on
1373  if ( mMapTool )
1374  {
1375  mMapTool->keyReleaseEvent( e );
1376  }
1377  else e->ignore();
1378 
1379  QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
1380  }
1381 
1382  emit keyReleased( e );
1383 
1384 } //keyReleaseEvent()
1385 
1386 
1388 {
1389  // call handler of current map tool
1390  if ( mMapTool )
1391  {
1392  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1393  mMapTool->canvasDoubleClickEvent( me.get() );
1394  }
1395 }// mouseDoubleClickEvent
1396 
1397 
1398 void QgsMapCanvas::beginZoomRect( QPoint pos )
1399 {
1400  mZoomRect.setRect( 0, 0, 0, 0 );
1401  QApplication::setOverrideCursor( mZoomCursor );
1402  mZoomDragging = true;
1403  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1404  QColor color( Qt::blue );
1405  color.setAlpha( 63 );
1406  mZoomRubberBand->setColor( color );
1407  mZoomRect.setTopLeft( pos );
1408 }
1409 
1410 void QgsMapCanvas::endZoomRect( QPoint pos )
1411 {
1412  mZoomDragging = false;
1413  mZoomRubberBand.reset( nullptr );
1414  QApplication::restoreOverrideCursor();
1415 
1416  // store the rectangle
1417  mZoomRect.setRight( pos.x() );
1418  mZoomRect.setBottom( pos.y() );
1419 
1420  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1421  {
1422  //probably a mistake - would result in huge zoom!
1423  return;
1424  }
1425 
1426  //account for bottom right -> top left dragging
1427  mZoomRect = mZoomRect.normalized();
1428 
1429  // set center and zoom
1430  const QSize &zoomRectSize = mZoomRect.size();
1431  const QSize &canvasSize = mSettings.outputSize();
1432  double sfx = static_cast< double >( zoomRectSize.width() ) / canvasSize.width();
1433  double sfy = static_cast< double >( zoomRectSize.height() ) / canvasSize.height();
1434  double sf = std::max( sfx, sfy );
1435 
1436  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1437 
1438  zoomByFactor( sf, &c );
1439  refresh();
1440 }
1441 
1442 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1443 {
1444  //use middle mouse button for panning, map tools won't receive any events in that case
1445  if ( e->button() == Qt::MidButton )
1446  {
1447  mCanvasProperties->panSelectorDown = true;
1448  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1449  }
1450  else
1451  {
1452  // call handler of current map tool
1453  if ( mMapTool )
1454  {
1455  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1456  && e->modifiers() & Qt::ShiftModifier )
1457  {
1458  beginZoomRect( e->pos() );
1459  return;
1460  }
1461  else
1462  {
1463  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1464  mMapTool->canvasPressEvent( me.get() );
1465  }
1466  }
1467  }
1468 
1469  if ( mCanvasProperties->panSelectorDown )
1470  {
1471  return;
1472  }
1473 
1474  mCanvasProperties->mouseButtonDown = true;
1475  mCanvasProperties->rubberStartPoint = e->pos();
1476 
1477 } // mousePressEvent
1478 
1479 
1480 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1481 {
1482  //use middle mouse button for panning, map tools won't receive any events in that case
1483  if ( e->button() == Qt::MidButton )
1484  {
1485  mCanvasProperties->panSelectorDown = false;
1486  panActionEnd( mCanvasProperties->mouseLastXY );
1487  }
1488  else if ( e->button() == Qt::BackButton )
1489  {
1491  return;
1492  }
1493  else if ( e->button() == Qt::ForwardButton )
1494  {
1495  zoomToNextExtent();
1496  return;
1497  }
1498  else
1499  {
1500  if ( mZoomDragging && e->button() == Qt::LeftButton )
1501  {
1502  endZoomRect( e->pos() );
1503  return;
1504  }
1505 
1506  // call handler of current map tool
1507  if ( mMapTool )
1508  {
1509  // right button was pressed in zoom tool? return to previous non zoom tool
1510  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1511  {
1512  QgsDebugMsg( QStringLiteral( "Right click in map tool zoom or pan, last tool is %1." ).arg(
1513  mLastNonZoomMapTool ? QStringLiteral( "not null" ) : QStringLiteral( "null" ) ) );
1514 
1515  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1516 
1517  // change to older non-zoom tool
1518  if ( mLastNonZoomMapTool
1519  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1520  || ( vlayer && vlayer->isEditable() ) ) )
1521  {
1522  QgsMapTool *t = mLastNonZoomMapTool;
1523  mLastNonZoomMapTool = nullptr;
1524  setMapTool( t );
1525  }
1526  return;
1527  }
1528  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1529  mMapTool->canvasReleaseEvent( me.get() );
1530  }
1531  }
1532 
1533 
1534  mCanvasProperties->mouseButtonDown = false;
1535 
1536  if ( mCanvasProperties->panSelectorDown )
1537  return;
1538 
1539 } // mouseReleaseEvent
1540 
1541 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1542 {
1543  QGraphicsView::resizeEvent( e );
1544  mResizeTimer->start( 500 ); // in charge of refreshing canvas
1545 
1546  double oldScale = mSettings.scale();
1547  QSize lastSize = viewport()->size();
1548  mSettings.setOutputSize( lastSize );
1549 
1550  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1551 
1552  moveCanvasContents( true );
1553 
1554  if ( mScaleLocked )
1555  {
1556  double scaleFactor = oldScale / mSettings.scale();
1557  QgsRectangle r = mSettings.extent();
1558  QgsPointXY center = r.center();
1559  r.scale( scaleFactor, &center );
1560  mSettings.setExtent( r );
1561  }
1562  else
1563  {
1564  updateScale();
1565  }
1566 
1567  emit extentsChanged();
1568 }
1569 
1570 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1571 {
1572  // no custom event handling anymore
1573 
1574  QGraphicsView::paintEvent( e );
1575 } // paintEvent
1576 
1578 {
1579  QList<QGraphicsItem *> list = mScene->items();
1580  QList<QGraphicsItem *>::iterator it = list.begin();
1581  while ( it != list.end() )
1582  {
1583  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( *it );
1584 
1585  if ( item )
1586  {
1587  item->updatePosition();
1588  }
1589 
1590  ++it;
1591  }
1592 }
1593 
1594 
1595 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1596 {
1597  // Zoom the map canvas in response to a mouse wheel event. Moving the
1598  // wheel forward (away) from the user zooms in
1599 
1600  QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
1601 
1602  if ( mMapTool )
1603  {
1604  mMapTool->wheelEvent( e );
1605  if ( e->isAccepted() )
1606  return;
1607  }
1608 
1609  if ( e->delta() == 0 )
1610  {
1611  e->accept();
1612  return;
1613  }
1614 
1615  double zoomFactor = mWheelZoomFactor;
1616 
1617  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1618  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
1619 
1620  if ( e->modifiers() & Qt::ControlModifier )
1621  {
1622  //holding ctrl while wheel zooming results in a finer zoom
1623  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1624  }
1625 
1626  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1627 
1628  // zoom map to mouse cursor by scaling
1629  QgsPointXY oldCenter = center();
1630  QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->x(), e->y() ) );
1631  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1632  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1633 
1634  zoomByFactor( signedWheelFactor, &newCenter );
1635  e->accept();
1636 }
1637 
1638 void QgsMapCanvas::setWheelFactor( double factor )
1639 {
1640  mWheelZoomFactor = factor;
1641 }
1642 
1644 {
1645  // magnification is alreday handled in zoomByFactor
1646  zoomByFactor( 1 / mWheelZoomFactor );
1647 }
1648 
1650 {
1651  // magnification is alreday handled in zoomByFactor
1652  zoomByFactor( mWheelZoomFactor );
1653 }
1654 
1655 void QgsMapCanvas::zoomScale( double newScale )
1656 {
1657  zoomByFactor( newScale / scale() );
1658 }
1659 
1660 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1661 {
1662  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1663 
1664  if ( mScaleLocked )
1665  {
1667  }
1668  else
1669  {
1670  // transform the mouse pos to map coordinates
1673  r.scale( scaleFactor, &center );
1674  setExtent( r, true );
1675  refresh();
1676  }
1677 }
1678 
1679 void QgsMapCanvas::setScaleLocked( bool isLocked )
1680 {
1681  mScaleLocked = isLocked;
1682 }
1683 
1684 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1685 {
1686  mCanvasProperties->mouseLastXY = e->pos();
1687 
1688  if ( mCanvasProperties->panSelectorDown )
1689  {
1690  panAction( e );
1691  }
1692  else if ( mZoomDragging )
1693  {
1694  mZoomRect.setBottomRight( e->pos() );
1695  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1696  mZoomRubberBand->show();
1697  }
1698  else
1699  {
1700  // call handler of current map tool
1701  if ( mMapTool )
1702  {
1703  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1704  mMapTool->canvasMoveEvent( me.get() );
1705  }
1706  }
1707 
1708  // show x y on status bar
1709  mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
1710  emit xyCoordinates( mCursorPoint );
1711 }
1712 
1713 void QgsMapCanvas::setMapTool( QgsMapTool *tool, bool clean )
1714 {
1715  if ( !tool )
1716  return;
1717 
1718  if ( mMapTool )
1719  {
1720  if ( clean )
1721  mMapTool->clean();
1722 
1723  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1724  mMapTool->deactivate();
1725  }
1726 
1727  if ( ( tool->flags() & QgsMapTool::Transient )
1728  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1729  {
1730  // if zoom or pan tool will be active, save old tool
1731  // to bring it back on right click
1732  // (but only if it wasn't also zoom or pan tool)
1733  mLastNonZoomMapTool = mMapTool;
1734  }
1735  else
1736  {
1737  mLastNonZoomMapTool = nullptr;
1738  }
1739 
1740  QgsMapTool *oldTool = mMapTool;
1741 
1742  // set new map tool and activate it
1743  mMapTool = tool;
1744  if ( mMapTool )
1745  {
1746  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1747  mMapTool->activate();
1748  }
1749 
1750  emit mapToolSet( mMapTool, oldTool );
1751 } // setMapTool
1752 
1754 {
1755  if ( mMapTool && mMapTool == tool )
1756  {
1757  mMapTool->deactivate();
1758  mMapTool = nullptr;
1759  emit mapToolSet( nullptr, mMapTool );
1760  setCursor( Qt::ArrowCursor );
1761  }
1762 
1763  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1764  {
1765  mLastNonZoomMapTool = nullptr;
1766  }
1767 }
1768 
1769 void QgsMapCanvas::setCanvasColor( const QColor &color )
1770 {
1771  if ( canvasColor() == color )
1772  return;
1773 
1774  // background of map's pixmap
1775  mSettings.setBackgroundColor( color );
1776 
1777  // background of the QGraphicsView
1778  QBrush bgBrush( color );
1779  setBackgroundBrush( bgBrush );
1780 #if 0
1781  QPalette palette;
1782  palette.setColor( backgroundRole(), color );
1783  setPalette( palette );
1784 #endif
1785 
1786  // background of QGraphicsScene
1787  mScene->setBackgroundBrush( bgBrush );
1788 
1789  emit canvasColorChanged();
1790 }
1791 
1793 {
1794  return mScene->backgroundBrush().color();
1795 }
1796 
1797 void QgsMapCanvas::setSelectionColor( const QColor &color )
1798 {
1799  mSettings.setSelectionColor( color );
1800 }
1801 
1803 {
1804  return mSettings.selectionColor();
1805 }
1806 
1808 {
1809  return mapSettings().layers().size();
1810 } // layerCount
1811 
1812 
1813 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1814 {
1815  return mapSettings().layers();
1816 }
1817 
1819 {
1820  // called when a layer has changed visibility setting
1821  refresh();
1822 }
1823 
1824 void QgsMapCanvas::freeze( bool frozen )
1825 {
1826  mFrozen = frozen;
1827 }
1828 
1830 {
1831  return mFrozen;
1832 }
1833 
1835 {
1836  return mapSettings().mapUnitsPerPixel();
1837 }
1838 
1840 {
1841  return mapSettings().mapUnits();
1842 }
1843 
1844 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1845 {
1846  return mSettings.layerStyleOverrides();
1847 }
1848 
1849 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1850 {
1851  if ( overrides == mSettings.layerStyleOverrides() )
1852  return;
1853 
1854  mSettings.setLayerStyleOverrides( overrides );
1855  clearCache();
1857 }
1858 
1859 void QgsMapCanvas::setTheme( const QString &theme )
1860 {
1861  if ( mTheme == theme )
1862  return;
1863 
1864  clearCache();
1865  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1866  {
1867  mTheme.clear();
1868  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1869  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1870  emit themeChanged( QString() );
1871  }
1872  else
1873  {
1874  mTheme = theme;
1875  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1876  emit themeChanged( theme );
1877  }
1878 }
1879 
1881 {
1882  mRenderFlag = flag;
1883 
1884  if ( mRenderFlag )
1885  {
1886  refresh();
1887  }
1888  else
1889  stopRendering();
1890 }
1891 
1892 #if 0
1893 void QgsMapCanvas::connectNotify( const char *signal )
1894 {
1895  Q_UNUSED( signal );
1896  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1897 } //connectNotify
1898 #endif
1899 
1900 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1901 {
1902  if ( !deferred )
1903  refresh();
1904 }
1905 
1906 void QgsMapCanvas::autoRefreshTriggered()
1907 {
1908  if ( mJob )
1909  {
1910  // canvas is currently being redrawn, so we skip this auto refresh
1911  // otherwise we could get stuck in the situation where an auto refresh is triggered
1912  // too often to allow the canvas to ever finish rendering
1913  return;
1914  }
1915 
1916  refresh();
1917 }
1918 
1919 void QgsMapCanvas::updateAutoRefreshTimer()
1920 {
1921  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1922  // trigger a map refresh on this minimum interval
1923  int minAutoRefreshInterval = -1;
1924  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1925  {
1926  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1927  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1928  }
1929 
1930  if ( minAutoRefreshInterval > 0 )
1931  {
1932  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1933  mAutoRefreshTimer.start();
1934  }
1935  else
1936  {
1937  mAutoRefreshTimer.stop();
1938  }
1939 }
1940 
1941 void QgsMapCanvas::projectThemesChanged()
1942 {
1943  if ( mTheme.isEmpty() )
1944  return;
1945 
1946  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1947  {
1948  // theme has been removed - stop following
1949  setTheme( QString() );
1950  }
1951 
1952 }
1953 
1955 {
1956  return mMapTool;
1957 }
1958 
1959 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
1960 {
1961  // move map image and other items to standard position
1962  moveCanvasContents( true ); // true means reset
1963 
1964  // use start and end box points to calculate the extent
1965  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
1966  QgsPointXY end = getCoordinateTransform()->toMapCoordinates( releasePoint );
1967 
1968  // modify the center
1969  double dx = end.x() - start.x();
1970  double dy = end.y() - start.y();
1971  QgsPointXY c = center();
1972  c.set( c.x() - dx, c.y() - dy );
1973  setCenter( c );
1974 
1975  refresh();
1976 }
1977 
1978 void QgsMapCanvas::panAction( QMouseEvent *e )
1979 {
1980  Q_UNUSED( e );
1981 
1982  // move all map canvas items
1984 }
1985 
1987 {
1988  QPoint pnt( 0, 0 );
1989  if ( !reset )
1990  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
1991 
1992  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
1993 }
1994 
1996 {
1997  return mCanvasProperties->mouseLastXY;
1998 }
1999 
2000 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
2001 {
2002  if ( !mPreviewEffect )
2003  {
2004  return;
2005  }
2006 
2007  mPreviewEffect->setEnabled( previewEnabled );
2008 }
2009 
2011 {
2012  if ( !mPreviewEffect )
2013  {
2014  return false;
2015  }
2016 
2017  return mPreviewEffect->isEnabled();
2018 }
2019 
2021 {
2022  if ( !mPreviewEffect )
2023  {
2024  return;
2025  }
2026 
2027  mPreviewEffect->setMode( mode );
2028 }
2029 
2031 {
2032  if ( !mPreviewEffect )
2033  {
2035  }
2036 
2037  return mPreviewEffect->mode();
2038 }
2039 
2041 {
2042  if ( !mSnappingUtils )
2043  {
2044  // associate a dummy instance, but better than null pointer
2045  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
2046  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
2047  }
2048  return mSnappingUtils;
2049 }
2050 
2052 {
2053  mSnappingUtils = utils;
2054 }
2055 
2056 void QgsMapCanvas::readProject( const QDomDocument &doc )
2057 {
2058  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
2059  if ( nodes.count() )
2060  {
2061  QDomNode node = nodes.item( 0 );
2062 
2063  // Search the specific MapCanvas node using the name
2064  if ( nodes.count() > 1 )
2065  {
2066  for ( int i = 0; i < nodes.size(); ++i )
2067  {
2068  QDomElement elementNode = nodes.at( i ).toElement();
2069 
2070  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
2071  {
2072  node = nodes.at( i );
2073  break;
2074  }
2075  }
2076  }
2077 
2078  QgsMapSettings tmpSettings;
2079  tmpSettings.readXml( node );
2080  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
2081  {
2082  // never manually set the crs for the main canvas - this is instead connected to the project CRS
2083  setDestinationCrs( tmpSettings.destinationCrs() );
2084  }
2085  setExtent( tmpSettings.extent() );
2086  setRotation( tmpSettings.rotation() );
2088 
2089  clearExtentHistory(); // clear the extent history on project load
2090 
2091  QDomElement elem = node.toElement();
2092  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
2093  {
2094  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
2095  {
2096  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
2097  }
2098  }
2099  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
2100  }
2101  else
2102  {
2103  QgsDebugMsg( QStringLiteral( "Couldn't read mapcanvas information from project" ) );
2104  }
2105 }
2106 
2107 void QgsMapCanvas::writeProject( QDomDocument &doc )
2108 {
2109  // create node "mapcanvas" and call mMapRenderer->writeXml()
2110 
2111  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
2112  if ( !nl.count() )
2113  {
2114  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
2115  return;
2116  }
2117  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
2118 
2119  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
2120  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
2121  if ( !mTheme.isEmpty() )
2122  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
2123  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
2124  qgisNode.appendChild( mapcanvasNode );
2125 
2126  mSettings.writeXml( mapcanvasNode, doc );
2127  // TODO: store only units, extent, projections, dest CRS
2128 }
2129 
2130 #if 0
2131 void QgsMapCanvas::getDatumTransformInfo( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination )
2132 {
2133  if ( !source.isValid() || !destination.isValid() )
2134  return;
2135 
2136  //check if default datum transformation available
2137  QgsSettings s;
2138  QString settingsString = "/Projections/" + source.authid() + "//" + destination.authid();
2139  QVariant defaultSrcTransform = s.value( settingsString + "_srcTransform" );
2140  QVariant defaultDestTransform = s.value( settingsString + "_destTransform" );
2141  if ( defaultSrcTransform.isValid() && defaultDestTransform.isValid() )
2142  {
2143  int sourceDatumTransform = defaultSrcTransform.toInt();
2144  int destinationDatumTransform = defaultDestTransform.toInt();
2145 
2147  context.addSourceDestinationDatumTransform( source, destination, sourceDatumTransform, destinationDatumTransform );
2149  return;
2150  }
2151 
2152  if ( !s.value( QStringLiteral( "/Projections/showDatumTransformDialog" ), false ).toBool() )
2153  {
2154  return;
2155  }
2156 
2157  //if several possibilities: present dialog
2158  QgsDatumTransformDialog d( source, destination );
2159  if ( d.availableTransformationCount() > 1 )
2160  d.exec();
2161 }
2162 #endif
2163 
2164 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center )
2165 {
2166  if ( mScaleLocked )
2167  {
2168  // zoom map to mouse cursor by magnifying
2170  }
2171  else
2172  {
2174  r.scale( scaleFactor, center );
2175  setExtent( r, true );
2176  refresh();
2177  }
2178 }
2179 
2181 {
2182  // Find out which layer it was that sent the signal.
2183  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( sender() );
2184  if ( layer )
2185  {
2186  emit selectionChanged( layer );
2187  refresh();
2188  }
2189 }
2190 
2191 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *e )
2192 {
2193  // By default graphics view delegates the drag events to graphics items.
2194  // But we do not want that and by ignoring the drag enter we let the
2195  // parent (e.g. QgisApp) to handle drops of map layers etc.
2196  e->ignore();
2197 }
2198 
2199 void QgsMapCanvas::mapToolDestroyed()
2200 {
2201  QgsDebugMsg( QStringLiteral( "maptool destroyed" ) );
2202  mMapTool = nullptr;
2203 }
2204 
2205 bool QgsMapCanvas::event( QEvent *e )
2206 {
2207  if ( !QTouchDevice::devices().empty() )
2208  {
2209  if ( e->type() == QEvent::Gesture )
2210  {
2211  // call handler of current map tool
2212  if ( mMapTool )
2213  {
2214  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2215  }
2216  }
2217  }
2218 
2219  // pass other events to base class
2220  return QGraphicsView::event( e );
2221 }
2222 
2224 {
2225  // reload all layers in canvas
2226  for ( int i = 0; i < layerCount(); i++ )
2227  {
2228  QgsMapLayer *l = layer( i );
2229  if ( l )
2230  l->reload();
2231  }
2232 
2233  // clear the cache
2234  clearCache();
2235 
2236  // and then refresh
2237  refresh();
2238 }
2239 
2241 {
2242  while ( mRefreshScheduled || mJob )
2243  {
2244  QgsApplication::processEvents();
2245  }
2246 }
2247 
2249 {
2250  mSettings.setSegmentationTolerance( tolerance );
2251 }
2252 
2254 {
2255  mSettings.setSegmentationToleranceType( type );
2256 }
2257 
2258 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2259 {
2260  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2261  QList<QGraphicsItem *> itemList = mScene->items();
2262  QList<QGraphicsItem *>::iterator it = itemList.begin();
2263  for ( ; it != itemList.end(); ++it )
2264  {
2265  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( *it );
2266  if ( aItem )
2267  {
2268  annotationItemList.push_back( aItem );
2269  }
2270  }
2271 
2272  return annotationItemList;
2273 }
2274 
2276 {
2277  mAnnotationsVisible = show;
2278  Q_FOREACH ( QgsMapCanvasAnnotationItem *item, annotationItems() )
2279  {
2280  item->setVisible( show );
2281  }
2282 }
2283 
2285 {
2286  mSettings.setLabelingEngineSettings( settings );
2287 }
2288 
2290 {
2291  return mSettings.labelingEngineSettings();
2292 }
2293 
2294 void QgsMapCanvas::startPreviewJobs()
2295 {
2296  stopPreviewJobs(); //just in case still running
2297  schedulePreviewJob( 0 );
2298 }
2299 
2300 void QgsMapCanvas::startPreviewJob( int number )
2301 {
2302  QgsRectangle mapRect = mSettings.visibleExtent();
2303 
2304  if ( number == 4 )
2305  number += 1;
2306 
2307  int j = number / 3;
2308  int i = number % 3;
2309 
2310  //copy settings, only update extent
2311  QgsMapSettings jobSettings = mSettings;
2312 
2313  double dx = ( i - 1 ) * mapRect.width();
2314  double dy = ( 1 - j ) * mapRect.height();
2315  QgsRectangle jobExtent = mapRect;
2316 
2317  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
2318  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
2319  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
2320  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
2321 
2322  jobSettings.setExtent( jobExtent );
2323  jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
2324  jobSettings.setFlag( QgsMapSettings::RenderPreviewJob, true );
2325 
2326  // truncate preview layers to fast layers
2327  const QList<QgsMapLayer *> layers = jobSettings.layers();
2328  QList< QgsMapLayer * > previewLayers;
2330  context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
2331  for ( QgsMapLayer *layer : layers )
2332  {
2333  context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
2334  if ( !layer->dataProvider()->renderInPreview( context ) )
2335  {
2336  QgsDebugMsgLevel( QStringLiteral( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
2337  continue;
2338  }
2339 
2340  previewLayers << layer;
2341  }
2342  jobSettings.setLayers( previewLayers );
2343 
2344  QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
2345  job->setProperty( "number", number );
2346  mPreviewJobs.append( job );
2347  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2348  job->start();
2349 }
2350 
2351 void QgsMapCanvas::stopPreviewJobs()
2352 {
2353  mPreviewTimer.stop();
2354  QList< QgsMapRendererQImageJob * >::const_iterator it = mPreviewJobs.constBegin();
2355  for ( ; it != mPreviewJobs.constEnd(); ++it )
2356  {
2357  if ( *it )
2358  {
2359  disconnect( *it, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2360  connect( *it, &QgsMapRendererQImageJob::finished, *it, &QgsMapRendererQImageJob::deleteLater );
2361  ( *it )->cancelWithoutBlocking();
2362  }
2363  }
2364  mPreviewJobs.clear();
2365 }
2366 
2367 void QgsMapCanvas::schedulePreviewJob( int number )
2368 {
2369  mPreviewTimer.setSingleShot( true );
2370  mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
2371  disconnect( mPreviewTimerConnection );
2372  mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
2373  {
2374  startPreviewJob( number );
2375  } );
2376  mPreviewTimer.start();
2377 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QgsRectangle boundingBoxOfSelected() const
Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,0,0,0) is returned.
QgsUnitTypes::DistanceUnit mapUnits() const
Gets units of map&#39;s geographical coordinates - used for scale calculation.
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
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:68
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:119
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
QPoint mouseLastXY
Last seen point of the mouse.
virtual bool isEmpty() const
Returns true if the geometry is empty.
QColor canvasColor() const
Read property of QColor bgColor.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
Base class for all map layer types.
Definition: qgsmaplayer.h:63
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
Job implementation that renders everything sequentially using a custom painter.
std::unique_ptr< CanvasProperties > mCanvasProperties
Handle pattern for implementation object.
Definition: qgsmapcanvas.h:883
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.
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:425
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
void zoomToNextExtent()
Zoom to the next extent (view)
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:150
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...
double scale() const
Returns the calculated map scale.
bool isFrozen() const
Returns true if canvas is frozen.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
int layerCount() const
Returns number of layers on the map.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:134
void clearExtentHistory()
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer&#39;s CRS to output CRS
QgsRectangle fullExtent() const
returns current extent of layer set
Maximum angle between generating radii (lines from arc center to output vertices) ...
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
bool event(QEvent *e) override
Overridden standard event to be gestures aware.
bool mouseButtonDown
Flag to indicate status of mouse button.
void wheelEvent(QWheelEvent *e) override
Overridden mouse wheel event.
bool previewJobsEnabled() const
Returns true if canvas map preview jobs (low priority render jobs which render portions of the view j...
void canvasColorChanged()
Emitted when canvas background color changes.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QColor selectionColor() const
Gets color that is used for drawing of selected vector features.
void stopRendering()
stop rendering (if there is any right now)
double rotation() const
Gets the current map canvas rotation in clockwise degrees.
float devicePixelRatio() const
Returns device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" di...
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...
A class to represent a 2D point.
Definition: qgspointxy.h:43
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:234
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:278
void keyPressEvent(QKeyEvent *e) override
Overridden key press event.
void zoomToFeatureExtent(QgsRectangle &rect)
Zooms to feature extent.
virtual void reload()
Synchronises with changes in the datasource.
Definition: qgsmaplayer.h:457
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
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:435
An abstract class for items that can be placed on the map canvas.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
void setCurrentLayer(QgsMapLayer *layer)
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:169
bool previewModeEnabled() const
Returns whether a preview mode is enabled for the map canvas.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
QList< QgsMapLayer * > layers() const
Returns the list of layers shown within the map canvas.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
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.
void setFlags(QgsMapSettings::Flags flags)
Sets combination of flags that will be used for rendering.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
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:106
void renderComplete(QPainter *)
Emitted when the canvas has rendered.
Snapping utils instance that is connected to a canvas and updates the configuration (map settings + c...
const QgsMapToPixel & mapToPixel() const
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
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.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsLabelingResults * labelingResults() const
Gets access to the labeling results (may be null)
const QgsCoordinateReferenceSystem & crs
void mousePressEvent(QMouseEvent *e) override
Overridden mouse press event.
QMap< QString, QString > layerStyleOverrides() const
Returns the stored overrides of styles for layers.
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...
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:74
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.
double magnificationFactor() const
Returns the magnification factor.
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
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
void magnificationChanged(double)
Emitted when the scale of the map changes.
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.
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.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers that should be shown in the canvas.
Deprecated to be deleted, stuff from here should be moved elsewhere.
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
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
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:36
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 hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsPointXY center() const
Gets map center, in geographical coordinates.
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.
double magnificationFactor() const
Returns the magnification factor.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
void start() override
Start the rendering job and immediately return.
void saveAsImage(const QString &fileName, QPixmap *QPixmap=nullptr, const QString &="PNG")
Save the convtents of the map canvas to disk as an image.
QgsMapThemeCollection mapThemeCollection
Definition: qgsproject.h:98
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global labeling engine settings in the internal map settings.
void mapCanvasRefreshed()
Emitted when canvas finished a refresh request.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
void rotationChanged(double)
Emitted when the rotation of the map changes.
QSize outputSize() const
Returns the size of the resulting map image.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
#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.
A circle is used to highlight points (○)
Definition: qgsrubberband.h:94
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...
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled) ...
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:47
Job implementation that renders all layers in parallel.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:139
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.
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.
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:801
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:229
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Centers canvas extent to feature ids.
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.
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
void setWheelFactor(double factor)
Sets wheel zoom factor (should be greater than 1)
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
void destinationCrsChanged()
Emitted when map CRS has changed.
double mapUnitsPerPixel() const
Returns current map units per pixel.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds) ...
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed.
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
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
Single scope for storing variables and functions for use within a QgsExpressionContext.
Contains information about the context in which a coordinate transform is executed.
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
void transformContextChanged()
Emitted when the canvas transform context is changed.
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.
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.
QgsPreviewEffect::PreviewMode previewMode() const
Returns the current preview mode for the map canvas.
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.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns global labeling engine settings from the internal map settings.
bool addSourceDestinationDatumTransform(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransformId, int destinationTransformId)
Adds a new sourceTransform and destinationTransform to use when projecting coordinates from the speci...
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:28
QColor backgroundColor() const
Gets the background color of the map.
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.
QPoint mouseLastXY()
returns last position of mouse cursor
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
void mouseMoveEvent(QMouseEvent *e) override
Overridden mouse move event.
void keyReleaseEvent(QKeyEvent *e) override
Overridden key release event.
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
int availableTransformationCount()
Returns the number of possible datum transformation for currently selected source and destination CRS...
virtual void keyPressEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:182
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:53
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
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.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:96
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:358
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:138
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...
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
double scale() const
Returns the last reported scale of the canvas.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
QString what() const
Definition: qgsexception.h:48
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:225
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...
QColor selectionColor() const
Returns color for selected features.
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:144
void writeProject(QDomDocument &)
called to write map canvas settings to project
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
Intermediate base class adding functionality that allows client to query the rendered image...
Stores global configuration for labeling engine.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:411
void zoomToFullExtent()
Zoom to the full extent of all layers.
This class represents a coordinate reference system (CRS).
This class has all the configuration of snapping and can return answers to snapping queries...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:652
void setMapSettingsFlags(QgsMapSettings::Flags flags)
Resets the flags for the canvas&#39; map settings.
void refreshAllLayers()
Reload all layers, clear the cache and refresh the canvas.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
QString authid() const
Returns the authority identifier for the CRS.
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.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
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:235
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
QgsUnitTypes::DistanceUnit mapUnits() const
Convenience function for returning the current canvas map units.
virtual void cancelWithoutBlocking()=0
Triggers cancellation of the rendering job without blocking.
void setSelectionColor(const QColor &color)
Set color of selected vector features.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query 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.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
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.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
This class is responsible for keeping cache of rendered images resulting from a map rendering job...
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.
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider.
PreviewMode mode() const
Returns the mode used for the preview effect.
void readXml(QDomNode &node)
bool nextFeature(QgsFeature &f)
QPoint rubberStartPoint
Beginning point of a rubber band.
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
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.
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
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
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
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QString theme() const
Returns the map&#39;s theme shown in the canvas, if set.
Definition: qgsmapcanvas.h:424
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:102
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:129
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:70
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:208
void setMagnificationFactor(double factor)
Sets the factor of magnification to apply to the map canvas.
void panActionEnd(QPoint releasePoint)
Ends pan action and redraws the canvas.
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
void setLayers(const QList< QgsMapLayer * > &layers)
Set list of layers for map rendering.
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