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