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