QGIS API Documentation  3.4.3-Madeira (2f64a3c)
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 
19 #include <QtGlobal>
20 #include <QApplication>
21 #include <QCursor>
22 #include <QDir>
23 #include <QFile>
24 #include <QGraphicsItem>
25 #include <QGraphicsScene>
26 #include <QGraphicsView>
27 #include <QKeyEvent>
28 #include <QPainter>
29 #include <QPaintEvent>
30 #include <QPixmap>
31 #include <QRect>
32 #include <QTextStream>
33 #include <QResizeEvent>
34 #include <QScreen>
35 #include <QString>
36 #include <QStringList>
37 #include <QWheelEvent>
38 #include <QWindow>
39 
40 #include "qgis.h"
41 #include "qgssettings.h"
43 #include "qgsapplication.h"
44 #include "qgsexception.h"
46 #include "qgsfeatureiterator.h"
47 #include "qgslogger.h"
48 #include "qgsmapcanvas.h"
49 #include "qgsmapcanvasmap.h"
51 #include "qgsmaplayer.h"
52 #include "qgsmapmouseevent.h"
53 #include "qgsmaptoolpan.h"
54 #include "qgsmaptoolzoom.h"
55 #include "qgsmaptopixel.h"
56 #include "qgsmapoverviewcanvas.h"
57 #include "qgsmaprenderercache.h"
61 #include "qgsmapsettingsutils.h"
62 #include "qgsmessagelog.h"
63 #include "qgsmessageviewer.h"
64 #include "qgspallabeling.h"
65 #include "qgsproject.h"
66 #include "qgsrubberband.h"
67 #include "qgsvectorlayer.h"
68 #include "qgsmapthemecollection.h"
70 #include "qgssvgcache.h"
71 #include <cmath>
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 } // refresh
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 center = mSettings.mapToLayerCoordinates( layer, rect.center() );
1008  QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, &center );
1010  QgsFeatureIterator fit = layer->getFeatures( req );
1011  QgsFeature f;
1012  QgsPointXY closestPoint;
1013  double closestSquaredDistance = extentRect.width() + extentRect.height();
1014  bool pointFound = false;
1015  while ( fit.nextFeature( f ) )
1016  {
1017  QgsPointXY point = f.geometry().asPoint();
1018  double sqrDist = point.sqrDist( center );
1019  if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits<double>::epsilon() )
1020  continue;
1021  pointFound = true;
1022  closestPoint = point;
1023  closestSquaredDistance = sqrDist;
1024  }
1025  if ( pointFound )
1026  {
1027  // combine selected point with closest point and scale this rect
1028  rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) );
1029  rect.scale( scaleFactor, &center );
1030  }
1031  }
1032 
1033  zoomToFeatureExtent( rect );
1034 }
1035 
1037 {
1038  // no selected features, only one selected point feature
1039  //or two point features with the same x- or y-coordinates
1040  if ( rect.isEmpty() )
1041  {
1042  // zoom in
1043  QgsPointXY c = rect.center();
1044  rect = extent();
1045  rect.scale( 1.0, &c );
1046  }
1047  //zoom to an area
1048  else
1049  {
1050  // Expand rect to give a bit of space around the selected
1051  // objects so as to keep them clear of the map boundaries
1052  // The same 5% should apply to all margins.
1053  rect.scale( 1.05 );
1054  }
1055 
1056  setExtent( rect );
1057  refresh();
1058 }
1059 
1061 {
1062  if ( !layer )
1063  {
1064  return;
1065  }
1066 
1067  QgsRectangle bbox;
1068  QString errorMsg;
1069  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1070  {
1071  zoomToFeatureExtent( bbox );
1072  }
1073  else
1074  {
1075  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, Qgis::Warning );
1076  }
1077 
1078 }
1079 
1081 {
1082  if ( !layer )
1083  {
1084  return;
1085  }
1086 
1087  QgsRectangle bbox;
1088  QString errorMsg;
1089  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1090  {
1091  setCenter( bbox.center() );
1092  refresh();
1093  }
1094  else
1095  {
1096  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, Qgis::Warning );
1097  }
1098 }
1099 
1100 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1101 {
1102  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1103  bbox.setMinimal();
1104  QgsFeature fet;
1105  int featureCount = 0;
1106  errorMsg.clear();
1107 
1108  while ( it.nextFeature( fet ) )
1109  {
1110  QgsGeometry geom = fet.geometry();
1111  if ( geom.isNull() )
1112  {
1113  errorMsg = tr( "Feature does not have a geometry" );
1114  }
1115  else if ( geom.constGet()->isEmpty() )
1116  {
1117  errorMsg = tr( "Feature geometry is empty" );
1118  }
1119  if ( !errorMsg.isEmpty() )
1120  {
1121  return false;
1122  }
1124  bbox.combineExtentWith( r );
1125  featureCount++;
1126  }
1127 
1128  if ( featureCount != ids.count() )
1129  {
1130  errorMsg = tr( "Feature not found" );
1131  return false;
1132  }
1133 
1134  return true;
1135 }
1136 
1138 {
1139  if ( !layer )
1140  {
1141  // use current layer by default
1142  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1143  }
1144 
1145  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1146  return;
1147 
1148  QgsRectangle rect = layer->boundingBoxOfSelected();
1149  if ( rect.isNull() )
1150  {
1151  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
1152  return;
1153  }
1154 
1155  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1156  setCenter( rect.center() );
1157  refresh();
1158 }
1159 
1161  const QColor &color1, const QColor &color2,
1162  int flashes, int duration )
1163 {
1164  if ( !layer )
1165  {
1166  return;
1167  }
1168 
1169  QList< QgsGeometry > geoms;
1170 
1171  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1172  QgsFeature fet;
1173  while ( it.nextFeature( fet ) )
1174  {
1175  if ( !fet.hasGeometry() )
1176  continue;
1177  geoms << fet.geometry();
1178  }
1179 
1180  flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
1181 }
1182 
1183 void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
1184 {
1185  if ( geometries.isEmpty() )
1186  return;
1187 
1188  QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
1189  QgsRubberBand *rb = new QgsRubberBand( this, geomType );
1190  for ( const QgsGeometry &geom : geometries )
1191  rb->addGeometry( geom, crs );
1192 
1193  if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
1194  {
1195  rb->setWidth( 2 );
1196  rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
1197  }
1198  if ( geomType == QgsWkbTypes::PointGeometry )
1199  rb->setIcon( QgsRubberBand::ICON_CIRCLE );
1200 
1201  QColor startColor = color1;
1202  if ( !startColor.isValid() )
1203  {
1204  if ( geomType == QgsWkbTypes::PolygonGeometry )
1205  {
1206  startColor = rb->fillColor();
1207  }
1208  else
1209  {
1210  startColor = rb->strokeColor();
1211  }
1212  startColor.setAlpha( 255 );
1213  }
1214  QColor endColor = color2;
1215  if ( !endColor.isValid() )
1216  {
1217  endColor = startColor;
1218  endColor.setAlpha( 0 );
1219  }
1220 
1221 
1222  QVariantAnimation *animation = new QVariantAnimation( this );
1223  connect( animation, &QVariantAnimation::finished, this, [animation, rb]
1224  {
1225  animation->deleteLater();
1226  delete rb;
1227  } );
1228  connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
1229  {
1230  QColor c = value.value<QColor>();
1231  if ( geomType == QgsWkbTypes::PolygonGeometry )
1232  {
1233  rb->setFillColor( c );
1234  }
1235  else
1236  {
1237  rb->setStrokeColor( c );
1238  QColor c = rb->secondaryStrokeColor();
1239  c.setAlpha( c.alpha() );
1240  rb->setSecondaryStrokeColor( c );
1241  }
1242  rb->update();
1243  } );
1244 
1245  animation->setDuration( duration * flashes );
1246  animation->setStartValue( endColor );
1247  double midStep = 0.2 / flashes;
1248  for ( int i = 0; i < flashes; ++i )
1249  {
1250  double start = static_cast< double >( i ) / flashes;
1251  animation->setKeyValueAt( start + midStep, startColor );
1252  double end = static_cast< double >( i + 1 ) / flashes;
1253  if ( !qgsDoubleNear( end, 1.0 ) )
1254  animation->setKeyValueAt( end, endColor );
1255  }
1256  animation->setEndValue( endColor );
1257  animation->start();
1258 }
1259 
1260 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1261 {
1262  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1263  {
1264  emit keyPressed( e );
1265  return;
1266  }
1267 
1268  if ( ! mCanvasProperties->mouseButtonDown )
1269  {
1270  // Don't want to interfer with mouse events
1271 
1272  QgsRectangle currentExtent = mapSettings().visibleExtent();
1273  double dx = std::fabs( currentExtent.width() / 4 );
1274  double dy = std::fabs( currentExtent.height() / 4 );
1275 
1276  switch ( e->key() )
1277  {
1278  case Qt::Key_Left:
1279  QgsDebugMsg( QStringLiteral( "Pan left" ) );
1280  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1281  refresh();
1282  break;
1283 
1284  case Qt::Key_Right:
1285  QgsDebugMsg( QStringLiteral( "Pan right" ) );
1286  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1287  refresh();
1288  break;
1289 
1290  case Qt::Key_Up:
1291  QgsDebugMsg( QStringLiteral( "Pan up" ) );
1292  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1293  refresh();
1294  break;
1295 
1296  case Qt::Key_Down:
1297  QgsDebugMsg( QStringLiteral( "Pan down" ) );
1298  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1299  refresh();
1300  break;
1301 
1302 
1303 
1304  case Qt::Key_Space:
1305  QgsDebugMsg( QStringLiteral( "Pressing pan selector" ) );
1306 
1307  //mCanvasProperties->dragging = true;
1308  if ( ! e->isAutoRepeat() )
1309  {
1310  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1311  mCanvasProperties->panSelectorDown = true;
1312  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1313  }
1314  break;
1315 
1316  case Qt::Key_PageUp:
1317  QgsDebugMsg( QStringLiteral( "Zoom in" ) );
1318  zoomIn();
1319  break;
1320 
1321  case Qt::Key_PageDown:
1322  QgsDebugMsg( QStringLiteral( "Zoom out" ) );
1323  zoomOut();
1324  break;
1325 
1326 #if 0
1327  case Qt::Key_P:
1328  mUseParallelRendering = !mUseParallelRendering;
1329  refresh();
1330  break;
1331 
1332  case Qt::Key_S:
1333  mDrawRenderingStats = !mDrawRenderingStats;
1334  refresh();
1335  break;
1336 #endif
1337 
1338  default:
1339  // Pass it on
1340  if ( mMapTool )
1341  {
1342  mMapTool->keyPressEvent( e );
1343  }
1344  else e->ignore();
1345 
1346  QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
1347  }
1348  }
1349 
1350  emit keyPressed( e );
1351 
1352 } //keyPressEvent()
1353 
1354 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1355 {
1356  QgsDebugMsg( QStringLiteral( "keyRelease event" ) );
1357 
1358  switch ( e->key() )
1359  {
1360  case Qt::Key_Space:
1361  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1362  {
1363  QgsDebugMsg( QStringLiteral( "Releasing pan selector" ) );
1364  QApplication::restoreOverrideCursor();
1365  mCanvasProperties->panSelectorDown = false;
1366  panActionEnd( mCanvasProperties->mouseLastXY );
1367  }
1368  break;
1369 
1370  default:
1371  // Pass it on
1372  if ( mMapTool )
1373  {
1374  mMapTool->keyReleaseEvent( e );
1375  }
1376  else e->ignore();
1377 
1378  QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
1379  }
1380 
1381  emit keyReleased( e );
1382 
1383 } //keyReleaseEvent()
1384 
1385 
1387 {
1388  // call handler of current map tool
1389  if ( mMapTool )
1390  {
1391  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1392  mMapTool->canvasDoubleClickEvent( me.get() );
1393  }
1394 }// mouseDoubleClickEvent
1395 
1396 
1397 void QgsMapCanvas::beginZoomRect( QPoint pos )
1398 {
1399  mZoomRect.setRect( 0, 0, 0, 0 );
1400  QApplication::setOverrideCursor( mZoomCursor );
1401  mZoomDragging = true;
1402  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1403  QColor color( Qt::blue );
1404  color.setAlpha( 63 );
1405  mZoomRubberBand->setColor( color );
1406  mZoomRect.setTopLeft( pos );
1407 }
1408 
1409 void QgsMapCanvas::endZoomRect( QPoint pos )
1410 {
1411  mZoomDragging = false;
1412  mZoomRubberBand.reset( nullptr );
1413  QApplication::restoreOverrideCursor();
1414 
1415  // store the rectangle
1416  mZoomRect.setRight( pos.x() );
1417  mZoomRect.setBottom( pos.y() );
1418 
1419  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1420  {
1421  //probably a mistake - would result in huge zoom!
1422  return;
1423  }
1424 
1425  //account for bottom right -> top left dragging
1426  mZoomRect = mZoomRect.normalized();
1427 
1428  // set center and zoom
1429  const QSize &zoomRectSize = mZoomRect.size();
1430  const QSize &canvasSize = mSettings.outputSize();
1431  double sfx = static_cast< double >( zoomRectSize.width() ) / canvasSize.width();
1432  double sfy = static_cast< double >( zoomRectSize.height() ) / canvasSize.height();
1433  double sf = std::max( sfx, sfy );
1434 
1435  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1436 
1437  zoomByFactor( sf, &c );
1438  refresh();
1439 }
1440 
1441 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1442 {
1443  //use middle mouse button for panning, map tools won't receive any events in that case
1444  if ( e->button() == Qt::MidButton )
1445  {
1446  mCanvasProperties->panSelectorDown = true;
1447  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1448  }
1449  else
1450  {
1451  // call handler of current map tool
1452  if ( mMapTool )
1453  {
1454  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1455  && e->modifiers() & Qt::ShiftModifier )
1456  {
1457  beginZoomRect( e->pos() );
1458  return;
1459  }
1460  else
1461  {
1462  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1463  mMapTool->canvasPressEvent( me.get() );
1464  }
1465  }
1466  }
1467 
1468  if ( mCanvasProperties->panSelectorDown )
1469  {
1470  return;
1471  }
1472 
1473  mCanvasProperties->mouseButtonDown = true;
1474  mCanvasProperties->rubberStartPoint = e->pos();
1475 
1476 } // mousePressEvent
1477 
1478 
1479 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1480 {
1481  //use middle mouse button for panning, map tools won't receive any events in that case
1482  if ( e->button() == Qt::MidButton )
1483  {
1484  mCanvasProperties->panSelectorDown = false;
1485  panActionEnd( mCanvasProperties->mouseLastXY );
1486  }
1487  else if ( e->button() == Qt::BackButton )
1488  {
1490  return;
1491  }
1492  else if ( e->button() == Qt::ForwardButton )
1493  {
1494  zoomToNextExtent();
1495  return;
1496  }
1497  else
1498  {
1499  if ( mZoomDragging && e->button() == Qt::LeftButton )
1500  {
1501  endZoomRect( e->pos() );
1502  return;
1503  }
1504 
1505  // call handler of current map tool
1506  if ( mMapTool )
1507  {
1508  // right button was pressed in zoom tool? return to previous non zoom tool
1509  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1510  {
1511  QgsDebugMsg( QStringLiteral( "Right click in map tool zoom or pan, last tool is %1." ).arg(
1512  mLastNonZoomMapTool ? QStringLiteral( "not null" ) : QStringLiteral( "null" ) ) );
1513 
1514  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1515 
1516  // change to older non-zoom tool
1517  if ( mLastNonZoomMapTool
1518  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1519  || ( vlayer && vlayer->isEditable() ) ) )
1520  {
1521  QgsMapTool *t = mLastNonZoomMapTool;
1522  mLastNonZoomMapTool = nullptr;
1523  setMapTool( t );
1524  }
1525  return;
1526  }
1527  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1528  mMapTool->canvasReleaseEvent( me.get() );
1529  }
1530  }
1531 
1532 
1533  mCanvasProperties->mouseButtonDown = false;
1534 
1535  if ( mCanvasProperties->panSelectorDown )
1536  return;
1537 
1538 } // mouseReleaseEvent
1539 
1540 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1541 {
1542  QGraphicsView::resizeEvent( e );
1543  mResizeTimer->start( 500 );
1544 
1545  QSize lastSize = viewport()->size();
1546 
1547  mSettings.setOutputSize( lastSize );
1548 
1549  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1550 
1551  moveCanvasContents( true );
1552 
1553  updateScale();
1554 
1555  //refresh();
1556 
1557  emit extentsChanged();
1558 }
1559 
1560 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1561 {
1562  // no custom event handling anymore
1563 
1564  QGraphicsView::paintEvent( e );
1565 } // paintEvent
1566 
1568 {
1569  QList<QGraphicsItem *> list = mScene->items();
1570  QList<QGraphicsItem *>::iterator it = list.begin();
1571  while ( it != list.end() )
1572  {
1573  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( *it );
1574 
1575  if ( item )
1576  {
1577  item->updatePosition();
1578  }
1579 
1580  ++it;
1581  }
1582 }
1583 
1584 
1585 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1586 {
1587  // Zoom the map canvas in response to a mouse wheel event. Moving the
1588  // wheel forward (away) from the user zooms in
1589 
1590  QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
1591 
1592  if ( mMapTool )
1593  {
1594  mMapTool->wheelEvent( e );
1595  if ( e->isAccepted() )
1596  return;
1597  }
1598 
1599  if ( e->delta() == 0 )
1600  {
1601  e->accept();
1602  return;
1603  }
1604 
1605  double zoomFactor = mWheelZoomFactor;
1606 
1607  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1608  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
1609 
1610  if ( e->modifiers() & Qt::ControlModifier )
1611  {
1612  //holding ctrl while wheel zooming results in a finer zoom
1613  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1614  }
1615 
1616  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1617 
1618  // zoom map to mouse cursor by scaling
1619  QgsPointXY oldCenter = center();
1620  QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->x(), e->y() ) );
1621  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1622  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1623 
1624  zoomByFactor( signedWheelFactor, &newCenter );
1625  e->accept();
1626 }
1627 
1628 void QgsMapCanvas::setWheelFactor( double factor )
1629 {
1630  mWheelZoomFactor = factor;
1631 }
1632 
1634 {
1635  // magnification is alreday handled in zoomByFactor
1636  zoomByFactor( 1 / mWheelZoomFactor );
1637 }
1638 
1640 {
1641  // magnification is alreday handled in zoomByFactor
1642  zoomByFactor( mWheelZoomFactor );
1643 }
1644 
1645 void QgsMapCanvas::zoomScale( double newScale )
1646 {
1647  zoomByFactor( newScale / scale() );
1648 }
1649 
1650 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1651 {
1652  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1653 
1654  if ( mScaleLocked )
1655  {
1657  }
1658  else
1659  {
1660  // transform the mouse pos to map coordinates
1663  r.scale( scaleFactor, &center );
1664  setExtent( r, true );
1665  refresh();
1666  }
1667 }
1668 
1669 void QgsMapCanvas::setScaleLocked( bool isLocked )
1670 {
1671  mScaleLocked = isLocked;
1672 }
1673 
1674 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1675 {
1676  mCanvasProperties->mouseLastXY = e->pos();
1677 
1678  if ( mCanvasProperties->panSelectorDown )
1679  {
1680  panAction( e );
1681  }
1682  else if ( mZoomDragging )
1683  {
1684  mZoomRect.setBottomRight( e->pos() );
1685  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1686  mZoomRubberBand->show();
1687  }
1688  else
1689  {
1690  // call handler of current map tool
1691  if ( mMapTool )
1692  {
1693  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1694  mMapTool->canvasMoveEvent( me.get() );
1695  }
1696  }
1697 
1698  // show x y on status bar
1699  mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
1700  emit xyCoordinates( mCursorPoint );
1701 }
1702 
1703 void QgsMapCanvas::setMapTool( QgsMapTool *tool, bool clean )
1704 {
1705  if ( !tool )
1706  return;
1707 
1708  if ( mMapTool )
1709  {
1710  if ( clean )
1711  mMapTool->clean();
1712 
1713  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1714  mMapTool->deactivate();
1715  }
1716 
1717  if ( ( tool->flags() & QgsMapTool::Transient )
1718  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1719  {
1720  // if zoom or pan tool will be active, save old tool
1721  // to bring it back on right click
1722  // (but only if it wasn't also zoom or pan tool)
1723  mLastNonZoomMapTool = mMapTool;
1724  }
1725  else
1726  {
1727  mLastNonZoomMapTool = nullptr;
1728  }
1729 
1730  QgsMapTool *oldTool = mMapTool;
1731 
1732  // set new map tool and activate it
1733  mMapTool = tool;
1734  if ( mMapTool )
1735  {
1736  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1737  mMapTool->activate();
1738  }
1739 
1740  emit mapToolSet( mMapTool, oldTool );
1741 } // setMapTool
1742 
1744 {
1745  if ( mMapTool && mMapTool == tool )
1746  {
1747  mMapTool->deactivate();
1748  mMapTool = nullptr;
1749  emit mapToolSet( nullptr, mMapTool );
1750  setCursor( Qt::ArrowCursor );
1751  }
1752 
1753  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1754  {
1755  mLastNonZoomMapTool = nullptr;
1756  }
1757 }
1758 
1759 void QgsMapCanvas::setCanvasColor( const QColor &color )
1760 {
1761  if ( canvasColor() == color )
1762  return;
1763 
1764  // background of map's pixmap
1765  mSettings.setBackgroundColor( color );
1766 
1767  // background of the QGraphicsView
1768  QBrush bgBrush( color );
1769  setBackgroundBrush( bgBrush );
1770 #if 0
1771  QPalette palette;
1772  palette.setColor( backgroundRole(), color );
1773  setPalette( palette );
1774 #endif
1775 
1776  // background of QGraphicsScene
1777  mScene->setBackgroundBrush( bgBrush );
1778 
1779  emit canvasColorChanged();
1780 }
1781 
1783 {
1784  return mScene->backgroundBrush().color();
1785 }
1786 
1787 void QgsMapCanvas::setSelectionColor( const QColor &color )
1788 {
1789  mSettings.setSelectionColor( color );
1790 }
1791 
1793 {
1794  return mSettings.selectionColor();
1795 }
1796 
1798 {
1799  return mapSettings().layers().size();
1800 } // layerCount
1801 
1802 
1803 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1804 {
1805  return mapSettings().layers();
1806 }
1807 
1809 {
1810  // called when a layer has changed visibility setting
1811  refresh();
1812 }
1813 
1814 void QgsMapCanvas::freeze( bool frozen )
1815 {
1816  mFrozen = frozen;
1817 }
1818 
1820 {
1821  return mFrozen;
1822 }
1823 
1825 {
1826  return mapSettings().mapUnitsPerPixel();
1827 }
1828 
1830 {
1831  return mapSettings().mapUnits();
1832 }
1833 
1834 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1835 {
1836  return mSettings.layerStyleOverrides();
1837 }
1838 
1839 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1840 {
1841  if ( overrides == mSettings.layerStyleOverrides() )
1842  return;
1843 
1844  mSettings.setLayerStyleOverrides( overrides );
1845  clearCache();
1847 }
1848 
1849 void QgsMapCanvas::setTheme( const QString &theme )
1850 {
1851  if ( mTheme == theme )
1852  return;
1853 
1854  clearCache();
1855  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1856  {
1857  mTheme.clear();
1858  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1859  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1860  emit themeChanged( QString() );
1861  }
1862  else
1863  {
1864  mTheme = theme;
1865  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1866  emit themeChanged( theme );
1867  }
1868 }
1869 
1871 {
1872  mRenderFlag = flag;
1873 
1874  if ( mRenderFlag )
1875  {
1876  refresh();
1877  }
1878  else
1879  stopRendering();
1880 }
1881 
1882 #if 0
1883 void QgsMapCanvas::connectNotify( const char *signal )
1884 {
1885  Q_UNUSED( signal );
1886  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1887 } //connectNotify
1888 #endif
1889 
1890 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1891 {
1892  if ( !deferred )
1893  refresh();
1894 }
1895 
1896 void QgsMapCanvas::autoRefreshTriggered()
1897 {
1898  if ( mJob )
1899  {
1900  // canvas is currently being redrawn, so we skip this auto refresh
1901  // otherwise we could get stuck in the situation where an auto refresh is triggered
1902  // too often to allow the canvas to ever finish rendering
1903  return;
1904  }
1905 
1906  refresh();
1907 }
1908 
1909 void QgsMapCanvas::updateAutoRefreshTimer()
1910 {
1911  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1912  // trigger a map refresh on this minimum interval
1913  int minAutoRefreshInterval = -1;
1914  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1915  {
1916  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1917  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1918  }
1919 
1920  if ( minAutoRefreshInterval > 0 )
1921  {
1922  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1923  mAutoRefreshTimer.start();
1924  }
1925  else
1926  {
1927  mAutoRefreshTimer.stop();
1928  }
1929 }
1930 
1931 void QgsMapCanvas::projectThemesChanged()
1932 {
1933  if ( mTheme.isEmpty() )
1934  return;
1935 
1936  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1937  {
1938  // theme has been removed - stop following
1939  setTheme( QString() );
1940  }
1941 
1942 }
1943 
1945 {
1946  return mMapTool;
1947 }
1948 
1949 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
1950 {
1951  // move map image and other items to standard position
1952  moveCanvasContents( true ); // true means reset
1953 
1954  // use start and end box points to calculate the extent
1955  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
1956  QgsPointXY end = getCoordinateTransform()->toMapCoordinates( releasePoint );
1957 
1958  // modify the center
1959  double dx = end.x() - start.x();
1960  double dy = end.y() - start.y();
1961  QgsPointXY c = center();
1962  c.set( c.x() - dx, c.y() - dy );
1963  setCenter( c );
1964 
1965  refresh();
1966 }
1967 
1968 void QgsMapCanvas::panAction( QMouseEvent *e )
1969 {
1970  Q_UNUSED( e );
1971 
1972  // move all map canvas items
1974 }
1975 
1977 {
1978  QPoint pnt( 0, 0 );
1979  if ( !reset )
1980  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
1981 
1982  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
1983 }
1984 
1986 {
1987  return mCanvasProperties->mouseLastXY;
1988 }
1989 
1990 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
1991 {
1992  if ( !mPreviewEffect )
1993  {
1994  return;
1995  }
1996 
1997  mPreviewEffect->setEnabled( previewEnabled );
1998 }
1999 
2001 {
2002  if ( !mPreviewEffect )
2003  {
2004  return false;
2005  }
2006 
2007  return mPreviewEffect->isEnabled();
2008 }
2009 
2011 {
2012  if ( !mPreviewEffect )
2013  {
2014  return;
2015  }
2016 
2017  mPreviewEffect->setMode( mode );
2018 }
2019 
2021 {
2022  if ( !mPreviewEffect )
2023  {
2025  }
2026 
2027  return mPreviewEffect->mode();
2028 }
2029 
2031 {
2032  if ( !mSnappingUtils )
2033  {
2034  // associate a dummy instance, but better than null pointer
2035  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
2036  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
2037  }
2038  return mSnappingUtils;
2039 }
2040 
2042 {
2043  mSnappingUtils = utils;
2044 }
2045 
2046 void QgsMapCanvas::readProject( const QDomDocument &doc )
2047 {
2048  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
2049  if ( nodes.count() )
2050  {
2051  QDomNode node = nodes.item( 0 );
2052 
2053  // Search the specific MapCanvas node using the name
2054  if ( nodes.count() > 1 )
2055  {
2056  for ( int i = 0; i < nodes.size(); ++i )
2057  {
2058  QDomElement elementNode = nodes.at( i ).toElement();
2059 
2060  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
2061  {
2062  node = nodes.at( i );
2063  break;
2064  }
2065  }
2066  }
2067 
2068  QgsMapSettings tmpSettings;
2069  tmpSettings.readXml( node );
2070  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
2071  {
2072  // never manually set the crs for the main canvas - this is instead connected to the project CRS
2073  setDestinationCrs( tmpSettings.destinationCrs() );
2074  }
2075  setExtent( tmpSettings.extent() );
2076  setRotation( tmpSettings.rotation() );
2078 
2079  clearExtentHistory(); // clear the extent history on project load
2080 
2081  QDomElement elem = node.toElement();
2082  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
2083  {
2084  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
2085  {
2086  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
2087  }
2088  }
2089  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
2090  }
2091  else
2092  {
2093  QgsDebugMsg( QStringLiteral( "Couldn't read mapcanvas information from project" ) );
2094  }
2095 }
2096 
2097 void QgsMapCanvas::writeProject( QDomDocument &doc )
2098 {
2099  // create node "mapcanvas" and call mMapRenderer->writeXml()
2100 
2101  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
2102  if ( !nl.count() )
2103  {
2104  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
2105  return;
2106  }
2107  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
2108 
2109  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
2110  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
2111  if ( !mTheme.isEmpty() )
2112  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
2113  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
2114  qgisNode.appendChild( mapcanvasNode );
2115 
2116  mSettings.writeXml( mapcanvasNode, doc );
2117  // TODO: store only units, extent, projections, dest CRS
2118 }
2119 
2120 #if 0
2121 void QgsMapCanvas::getDatumTransformInfo( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination )
2122 {
2123  if ( !source.isValid() || !destination.isValid() )
2124  return;
2125 
2126  //check if default datum transformation available
2127  QgsSettings s;
2128  QString settingsString = "/Projections/" + source.authid() + "//" + destination.authid();
2129  QVariant defaultSrcTransform = s.value( settingsString + "_srcTransform" );
2130  QVariant defaultDestTransform = s.value( settingsString + "_destTransform" );
2131  if ( defaultSrcTransform.isValid() && defaultDestTransform.isValid() )
2132  {
2133  int sourceDatumTransform = defaultSrcTransform.toInt();
2134  int destinationDatumTransform = defaultDestTransform.toInt();
2135 
2137  context.addSourceDestinationDatumTransform( source, destination, sourceDatumTransform, destinationDatumTransform );
2139  return;
2140  }
2141 
2142  if ( !s.value( QStringLiteral( "/Projections/showDatumTransformDialog" ), false ).toBool() )
2143  {
2144  return;
2145  }
2146 
2147  //if several possibilities: present dialog
2148  QgsDatumTransformDialog d( source, destination );
2149  if ( d.availableTransformationCount() > 1 )
2150  d.exec();
2151 }
2152 #endif
2153 
2154 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center )
2155 {
2156  if ( mScaleLocked )
2157  {
2158  // zoom map to mouse cursor by magnifying
2160  }
2161  else
2162  {
2164  r.scale( scaleFactor, center );
2165  setExtent( r, true );
2166  refresh();
2167  }
2168 }
2169 
2171 {
2172  // Find out which layer it was that sent the signal.
2173  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( sender() );
2174  if ( layer )
2175  {
2176  emit selectionChanged( layer );
2177  refresh();
2178  }
2179 }
2180 
2181 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *e )
2182 {
2183  // By default graphics view delegates the drag events to graphics items.
2184  // But we do not want that and by ignoring the drag enter we let the
2185  // parent (e.g. QgisApp) to handle drops of map layers etc.
2186  e->ignore();
2187 }
2188 
2189 void QgsMapCanvas::mapToolDestroyed()
2190 {
2191  QgsDebugMsg( QStringLiteral( "maptool destroyed" ) );
2192  mMapTool = nullptr;
2193 }
2194 
2195 bool QgsMapCanvas::event( QEvent *e )
2196 {
2197  if ( !QTouchDevice::devices().empty() )
2198  {
2199  if ( e->type() == QEvent::Gesture )
2200  {
2201  // call handler of current map tool
2202  if ( mMapTool )
2203  {
2204  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2205  }
2206  }
2207  }
2208 
2209  // pass other events to base class
2210  return QGraphicsView::event( e );
2211 }
2212 
2214 {
2215  // reload all layers in canvas
2216  for ( int i = 0; i < layerCount(); i++ )
2217  {
2218  QgsMapLayer *l = layer( i );
2219  if ( l )
2220  l->reload();
2221  }
2222 
2223  // clear the cache
2224  clearCache();
2225 
2226  // and then refresh
2227  refresh();
2228 }
2229 
2231 {
2232  while ( mRefreshScheduled || mJob )
2233  {
2234  QgsApplication::processEvents();
2235  }
2236 }
2237 
2239 {
2240  mSettings.setSegmentationTolerance( tolerance );
2241 }
2242 
2244 {
2245  mSettings.setSegmentationToleranceType( type );
2246 }
2247 
2248 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2249 {
2250  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2251  QList<QGraphicsItem *> itemList = mScene->items();
2252  QList<QGraphicsItem *>::iterator it = itemList.begin();
2253  for ( ; it != itemList.end(); ++it )
2254  {
2255  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( *it );
2256  if ( aItem )
2257  {
2258  annotationItemList.push_back( aItem );
2259  }
2260  }
2261 
2262  return annotationItemList;
2263 }
2264 
2266 {
2267  mAnnotationsVisible = show;
2268  Q_FOREACH ( QgsMapCanvasAnnotationItem *item, annotationItems() )
2269  {
2270  item->setVisible( show );
2271  }
2272 }
2273 
2275 {
2276  mSettings.setLabelingEngineSettings( settings );
2277 }
2278 
2280 {
2281  return mSettings.labelingEngineSettings();
2282 }
2283 
2284 void QgsMapCanvas::startPreviewJobs()
2285 {
2286  stopPreviewJobs(); //just in case still running
2287  schedulePreviewJob( 0 );
2288 }
2289 
2290 void QgsMapCanvas::startPreviewJob( int number )
2291 {
2292  QgsRectangle mapRect = mSettings.visibleExtent();
2293 
2294  if ( number == 4 )
2295  number += 1;
2296 
2297  int j = number / 3;
2298  int i = number % 3;
2299 
2300  //copy settings, only update extent
2301  QgsMapSettings jobSettings = mSettings;
2302 
2303  double dx = ( i - 1 ) * mapRect.width();
2304  double dy = ( 1 - j ) * mapRect.height();
2305  QgsRectangle jobExtent = mapRect;
2306 
2307  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
2308  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
2309  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
2310  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
2311 
2312  jobSettings.setExtent( jobExtent );
2313  jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
2314  jobSettings.setFlag( QgsMapSettings::RenderPreviewJob, true );
2315 
2316  // truncate preview layers to fast layers
2317  const QList<QgsMapLayer *> layers = jobSettings.layers();
2318  QList< QgsMapLayer * > previewLayers;
2320  context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
2321  for ( QgsMapLayer *layer : layers )
2322  {
2323  context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
2324  if ( !layer->dataProvider()->renderInPreview( context ) )
2325  {
2326  QgsDebugMsgLevel( QStringLiteral( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
2327  continue;
2328  }
2329 
2330  previewLayers << layer;
2331  }
2332  jobSettings.setLayers( previewLayers );
2333 
2334  QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
2335  job->setProperty( "number", number );
2336  mPreviewJobs.append( job );
2337  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2338  job->start();
2339 }
2340 
2341 void QgsMapCanvas::stopPreviewJobs()
2342 {
2343  mPreviewTimer.stop();
2344  QList< QgsMapRendererQImageJob * >::const_iterator it = mPreviewJobs.constBegin();
2345  for ( ; it != mPreviewJobs.constEnd(); ++it )
2346  {
2347  if ( *it )
2348  {
2349  disconnect( *it, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2350  connect( *it, &QgsMapRendererQImageJob::finished, *it, &QgsMapRendererQImageJob::deleteLater );
2351  ( *it )->cancelWithoutBlocking();
2352  }
2353  }
2354  mPreviewJobs.clear();
2355 }
2356 
2357 void QgsMapCanvas::schedulePreviewJob( int number )
2358 {
2359  mPreviewTimer.setSingleShot( true );
2360  mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
2361  disconnect( mPreviewTimerConnection );
2362  mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
2363  {
2364  startPreviewJob( number );
2365  } );
2366  mPreviewTimer.start();
2367 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
bool previewJobsEnabled() const
Returns true if canvas map preview jobs (low priority render jobs which render portions of the view j...
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.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns global labeling engine settings from the internal map settings.
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.
Job implementation that renders everything sequentially using a custom painter.
std::unique_ptr< CanvasProperties > mCanvasProperties
Handle pattern for implementation object.
Definition: qgsmapcanvas.h:883
virtual bool isEmpty() const
Returns true if the geometry is empty.
void setRotation(double degrees)
Set the rotation of the map canvas in clockwise degrees.
virtual void canvasMoveEvent(QgsMapMouseEvent *e)
Mouse move event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:156
void setRenderFlag(bool flag)
Sets whether a user has disabled canvas renders via the GUI.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds) ...
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
void zoomToNextExtent()
Zoom to the next extent (view)
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void zoomWithCenter(int x, int y, bool zoomIn)
Zooms in/out with a given center.
void setCanvasColor(const QColor &_newVal)
Write property of QColor bgColor.
void setCenter(const QgsPointXY &center)
Set the center of the map canvas, in geographical coordinates.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h: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...
QList< QgsMapLayer * > layers() const
Returns the list of layers shown within the map canvas.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
double magnificationFactor() const
Returns the magnification factor.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:134
void clearExtentHistory()
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
Maximum angle between generating radii (lines from arc center to output vertices) ...
bool event(QEvent *e) override
Overridden standard event to be gestures aware.
QColor selectionColor() const
Returns color for selected features.
bool mouseButtonDown
Flag to indicate status of mouse button.
void wheelEvent(QWheelEvent *e) override
Overridden mouse wheel event.
void canvasColorChanged()
Emitted when canvas background color changes.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void stopRendering()
stop rendering (if there is any right now)
double y
Definition: qgspointxy.h:48
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setPreviewJobsEnabled(bool enabled)
Sets whether canvas map preview jobs (low priority render jobs which render portions of the view just...
A class to represent a 2D point.
Definition: qgspointxy.h:43
double rotation() const
Gets the current map canvas rotation in clockwise degrees.
QgsPreviewEffect::PreviewMode previewMode() const
Returns the current preview mode for the map canvas.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h: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
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
QColor backgroundColor() const
Gets the background color of the map.
void keyPressEvent(QKeyEvent *e) override
Overridden key press event.
void zoomToFeatureExtent(QgsRectangle &rect)
Zooms to feature extent.
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
An abstract class for items that can be placed on the map canvas.
void setCurrentLayer(QgsMapLayer *layer)
int layerCount() const
Returns number of layers on the map.
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
void moveCanvasContents(bool reset=false)
called when panning is in action, reset indicates end of panning
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setFlags(QgsMapSettings::Flags flags)
Sets combination of flags that will be used for rendering.
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
constexpr double CANVAS_MAGNIFICATION_MAX
Maximum magnification level allowed in map canvases.
Definition: qgsguiutils.h:69
void readProject(const QDomDocument &)
called to read map canvas settings from project
bool panSelectorDown
Flag to indicate the pan selector key is held down by user.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h: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...
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QgsMapTool * mapTool()
Returns the currently active tool.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
void mousePressEvent(QMouseEvent *e) override
Overridden mouse press event.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:102
void mapThemesChanged()
Emitted when map themes within the collection are changed.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
virtual QImage renderedImage()=0
Gets a preview/resulting image.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:74
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
QgsExpressionContextScope * defaultExpressionContextScope()
Creates a new scope which contains default variables and functions relating to the map canvas...
void selectionChanged(QgsVectorLayer *layer)
Emitted when selection in any layer gets changed.
Enable drawing of labels on top of the map.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
double maxRenderingTimeMs
Default maximum allowable render time, in ms.
void zoomLastStatusChanged(bool)
Emitted when zoom last status changed.
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
virtual void canvasPressEvent(QgsMapMouseEvent *e)
Mouse press event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:166
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
void magnificationChanged(double)
Emitted when the scale of the map changes.
QString what() const
Definition: qgsexception.h:48
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void updateScale()
Emits signal scaleChanged to update scale in main window.
QgsUnitTypes::DistanceUnit mapUnits() const
Gets units of map&#39;s geographical coordinates - used for scale calculation.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
void resizeEvent(QResizeEvent *e) override
Overridden resize event.
Deprecated to be deleted, stuff from here should be moved elsewhere.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:169
void enableMapTileRendering(bool flag)
sets map tile rendering flag
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:82
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:186
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
QgsRectangle boundingBoxOfSelected() const
Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,0,0,0) is returned.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
Map tool is an edit tool, which can only be used when layer is editable.
Definition: qgsmaptool.h:93
void setOutputSize(QSize size)
Sets the size of the resulting map image.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
void start() override
Start the rendering job and immediately return.
void saveAsImage(const QString &fileName, QPixmap *QPixmap=nullptr, const QString &="PNG")
Save the 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.
void rotationChanged(double)
Emitted when the rotation of the map changes.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setMagnificationFactor(double factor)
Set the magnification factor.
void zoomNextStatusChanged(bool)
Emitted when zoom next status changed.
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:425
A circle is used to highlight points (○)
Definition: qgsrubberband.h:78
void flashFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of features with matching ids from a vector layer to flash within the canvas...
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled) ...
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:40
double scale() const
Returns the calculated map scale.
Job implementation that renders all layers in parallel.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
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.
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
void setDevicePixelRatio(float dpr)
Sets the device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" d...
void readProject(const QDomDocument &)
Emitted when a project is being read.
Enable anti-aliasing for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:665
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.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
void setWheelFactor(double factor)
Sets wheel zoom factor (should be greater than 1)
void destinationCrsChanged()
Emitted when map CRS has changed.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
bool isFrozen() const
Returns true if canvas is frozen.
void setCachingEnabled(bool enabled)
Set whether to cache images of rendered layers.
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:98
Single scope for storing variables and functions for use within a QgsExpressionContext.
Contains information about the context in which a coordinate transform is executed.
double mapUnitsPerPixel() const
Returns current map units per pixel.
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets a preview mode for the map canvas.
virtual void wheelEvent(QWheelEvent *e)
Mouse wheel event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:176
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
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.
float devicePixelRatio() const
Returns device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" di...
const QgsMapToPixel & mapToPixel() const
void keyPressed(QKeyEvent *e)
Emit key press event.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
void zoomOut()
Zoom out with fixed factor.
Enable drawing of vertex markers for layers in editing mode.
void waitWhileRendering()
Blocks until the rendering job has finished.
constexpr double CANVAS_MAGNIFICATION_MIN
Minimum magnification level allowed in map canvases.
Definition: qgsguiutils.h:61
void zoomToPreviousExtent()
Zoom to the previous extent (view)
bool isDrawing()
Find out whether rendering is in progress.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
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
PreviewMode mode() const
Returns the mode used for the preview effect.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
virtual void start()=0
Start the rendering job and immediately return.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QPoint mouseLastXY()
returns last position of mouse cursor
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
double magnificationFactor() const
Returns the magnification factor.
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QMap< QString, QString > layerStyleOverrides() const
Returns the stored overrides of styles for layers.
void mouseMoveEvent(QMouseEvent *e) override
Overridden mouse move event.
void keyReleaseEvent(QKeyEvent *e) override
Overridden key release event.
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:181
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:53
void flashGeometries(const QList< QgsGeometry > &geometries, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of geometries to flash within the canvas.
double scale() const
Returns the last reported scale of the canvas.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:96
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:358
QColor canvasColor() const
Read property of QColor bgColor.
void clear()
Invalidates the cache contents, clearing all cached images.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h: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...
QgsUnitTypes::DistanceUnit mapUnits() const
Convenience function for returning the current canvas map units.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void mouseReleaseEvent(QMouseEvent *e) override
Overridden mouse release event.
void writeProject(QDomDocument &)
Emitted when the project is being written.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:225
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
static QgsExpressionContextScope * atlasScope(QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QString theme() const
Returns the map&#39;s theme shown in the canvas, if set.
Definition: qgsmapcanvas.h:424
QgsPointXY center() const
Gets map center, in geographical coordinates.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
virtual bool gestureEvent(QGestureEvent *e)
gesture event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:191
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:161
void dragEnterEvent(QDragEnterEvent *e) override
Overridden drag enter event.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:144
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer&#39;s CRS to output CRS
void writeProject(QDomDocument &)
called to write map canvas settings to project
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
void setLayers(const QList< QgsMapLayer *> &layers)
Sets the list of layers that should be shown in the canvas.
QgsRectangle fullExtent() const
returns current extent of layer set
Intermediate base class adding functionality that allows client to query the rendered image...
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.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
This class has all the configuration of snapping and can return answers to snapping queries...
const QgsLabelingResults * labelingResults() const
Gets access to the labeling results (may be null)
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:652
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:435
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.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
void zoomScale(double scale)
Zooms the canvas to a specific scale.
Class for doing transforms between two map coordinate systems.
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
void 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...
virtual void cancelWithoutBlocking()=0
Triggers cancelation of the rendering job without blocking.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
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.
void freeze(bool frozen=true)
Freeze/thaw the map canvas.
void setScaleLocked(bool isLocked)
Lock the scale, so zooming can be performed using magnication.
Class that stores computed placement from labeling engine.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
This class is responsible for keeping cache of rendered images resulting from a map rendering job...
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QgsGeometry geometry
Definition: qgsfeature.h:67
void transformContextChanged()
Emitted when the project transformContext() is changed.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
QColor selectionColor() const
Gets color that is used for drawing of selected vector features.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:229
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider.
void readXml(QDomNode &node)
bool nextFeature(QgsFeature &f)
QPoint rubberStartPoint
Beginning point of a rubber band.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
virtual QgsLabelingResults * takeLabelingResults()=0
Gets pointer to internal labeling engine (in order to get access to the results). ...
void xyCoordinates(const QgsPointXY &p)
Emits current mouse position.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void writeXml(QDomNode &node, QDomDocument &doc)
void zoomIn()
Zoom in with fixed factor.
Stores settings related to the context in which a preview job runs.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
virtual void updatePosition()
called on changed extent or resize event to update position of the item
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
CanvasProperties()=default
Constructor for CanvasProperties.
An interactive map canvas item which displays a QgsAnnotation.
void themeChanged(const QString &theme)
Emitted when the canvas has been assigned a different map theme.
void extentsChanged()
Emitted when the extents of the map change.
virtual void clean()
convenient method to clean members
Definition: qgsmaptool.cpp:108
QSize outputSize() const
Returns the size of the resulting map image.
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
QgsMapLayer * layer(int index)
Returns the map layer at position index in the layer stack.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
QString authid() const
Returns the authority identifier for the CRS.
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.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
void panActionEnd(QPoint releasePoint)
Ends pan action and redraws the canvas.
bool previewModeEnabled() const
Returns whether a preview mode is enabled for the map canvas.
void layersChanged()
Emitted when a new set of layers has been received.
void messageEmitted(const QString &title, const QString &message, Qgis::MessageLevel=Qgis::Info)
emit a message (usually to be displayed in a message bar)
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
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:171