QGIS API Documentation  2.99.0-Master (dcec6bb)
qgsmapcanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 qgsmapcanvas.cpp - description
3 ------------------ -
4 begin : Sun Jun 30 2002
5 copyright : ( C ) 2002 by Gary E.Sherman
6 email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 #include <QtGlobal>
20 #include <QApplication>
21 #include <QCursor>
22 #include <QDir>
23 #include <QFile>
24 #include <QGraphicsItem>
25 #include <QGraphicsScene>
26 #include <QGraphicsView>
27 #include <QKeyEvent>
28 #include <QMouseEvent>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QPixmap>
32 #include <QRect>
33 #include <QTextStream>
34 #include <QResizeEvent>
35 #include <QString>
36 #include <QStringList>
37 #include <QWheelEvent>
38 
39 #include "qgis.h"
40 #include "qgssettings.h"
42 #include "qgsapplication.h"
43 #include "qgsexception.h"
45 #include "qgsfeatureiterator.h"
46 #include "qgslogger.h"
47 #include "qgsmapcanvas.h"
48 #include "qgsmapcanvasmap.h"
50 #include "qgsmaplayer.h"
51 #include "qgsmaptoolpan.h"
52 #include "qgsmaptoolzoom.h"
53 #include "qgsmaptopixel.h"
54 #include "qgsmapoverviewcanvas.h"
55 #include "qgsmaprenderercache.h"
59 #include "qgsmapsettingsutils.h"
60 #include "qgsmessagelog.h"
61 #include "qgsmessageviewer.h"
62 #include "qgspallabeling.h"
63 #include "qgsproject.h"
64 #include "qgsrubberband.h"
65 #include "qgsvectorlayer.h"
66 #include "qgscursors.h"
67 #include "qgsmapthemecollection.h"
68 #include <cmath>
69 
70 
75 //TODO QGIS 3.0 - remove
77 {
78  public:
80  : mouseButtonDown( false )
81  , panSelectorDown( false )
82  { }
83 
86 
88  QPoint mouseLastXY;
89 
92 
95 };
96 
97 
98 
99 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
100  : QGraphicsView( parent )
102  , mMap( nullptr )
103  , mFrozen( false )
104  , mRefreshScheduled( false )
105  , mRenderFlag( true ) // by default, the canvas is rendered
106  , mCurrentLayer( nullptr )
107  , mScene( nullptr )
108  , mMapTool( nullptr )
109  , mLastNonZoomMapTool( nullptr )
110  , mLastExtentIndex( -1 )
111  , mWheelZoomFactor( 2.0 )
112  , mJob( nullptr )
113  , mJobCanceled( false )
114  , mLabelingResults( nullptr )
115  , mUseParallelRendering( false )
116  , mDrawRenderingStats( false )
117  , mCache( nullptr )
118  , mResizeTimer( nullptr )
119  , mPreviewEffect( nullptr )
120  , mSnappingUtils( nullptr )
121  , mScaleLocked( false )
122  , mExpressionContextScope( tr( "Map Canvas" ) )
123  , mZoomDragging( false )
124 {
125  mScene = new QGraphicsScene();
126  setScene( mScene );
127  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
128  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
129  setMouseTracking( true );
130  setFocusPolicy( Qt::StrongFocus );
131 
132  mResizeTimer = new QTimer( this );
133  mResizeTimer->setSingleShot( true );
134  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
135 
136  mRefreshTimer = new QTimer( this );
137  mRefreshTimer->setSingleShot( true );
138  connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
139 
140  // create map canvas item which will show the map
141  mMap = new QgsMapCanvasMap( this );
142 
143  // project handling
145  this, &QgsMapCanvas::readProject );
148 
149  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
150  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
151 
155  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
157  this, [ = ]
158  {
159  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
160  } );
161 
162  //segmentation parameters
163  QgsSettings settings;
164  double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
165  QgsAbstractGeometry::SegmentationToleranceType toleranceType = QgsAbstractGeometry::SegmentationToleranceType( settings.value( QStringLiteral( "qgis/segmentationToleranceType" ), 0 ).toInt() );
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  setSceneRect( 0, 0, s.width(), s.height() );
174  mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
175 
176  moveCanvasContents( true );
177 
178  connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsMapCanvas::mapUpdateTimeout );
179  mMapUpdateTimer.setInterval( 250 );
180 
181 #ifdef Q_OS_WIN
182  // Enable touch event on Windows.
183  // Qt on Windows needs to be told it can take touch events or else it ignores them.
184  grabGesture( Qt::PinchGesture );
185  viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
186 #endif
187 
188  mPreviewEffect = new QgsPreviewEffect( this );
189  viewport()->setGraphicsEffect( mPreviewEffect );
190 
191  QPixmap zoomPixmap = QPixmap( ( const char ** )( zoom_in ) );
192  mZoomCursor = QCursor( zoomPixmap, 7, 7 );
193 
194  connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
195 
197 
198  setInteractive( false );
199 
200  refresh();
201 
202 } // QgsMapCanvas ctor
203 
204 
206 {
207  if ( mMapTool )
208  {
209  mMapTool->deactivate();
210  mMapTool = nullptr;
211  }
212  mLastNonZoomMapTool = nullptr;
213 
214  // rendering job may still end up writing into canvas map item
215  // so kill it before deleting canvas items
216  if ( mJob )
217  {
218  whileBlocking( mJob )->cancel();
219  delete mJob;
220  }
221 
222  // delete canvas items prior to deleting the canvas
223  // because they might try to update canvas when it's
224  // already being destructed, ends with segfault
225  QList<QGraphicsItem *> list = mScene->items();
226  QList<QGraphicsItem *>::iterator it = list.begin();
227  while ( it != list.end() )
228  {
229  QGraphicsItem *item = *it;
230  delete item;
231  ++it;
232  }
233 
234  mScene->deleteLater(); // crashes in python tests on windows
235 
236  // mCanvasProperties auto-deleted via QScopedPointer
237  // CanvasProperties struct has its own dtor for freeing resources
238 
239  delete mCache;
240 
241  delete mLabelingResults;
242 
243 } // dtor
244 
246 {
247  // do not go higher or lower than min max magnification ratio
248  double magnifierMin = QgsGuiUtils::CANVAS_MAGNIFICATION_MIN;
249  double magnifierMax = QgsGuiUtils::CANVAS_MAGNIFICATION_MAX;
250  factor = qBound( magnifierMin, factor, magnifierMax );
251 
252  // the magnifier widget is in integer percent
253  if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
254  {
255  mSettings.setMagnificationFactor( factor );
256  refresh();
257  emit magnificationChanged( factor );
258  }
259 }
260 
262 {
263  return mSettings.magnificationFactor();
264 }
265 
267 {
268  mSettings.setFlag( QgsMapSettings::Antialiasing, flag );
269 } // anti aliasing
270 
272 {
273  mSettings.setFlag( QgsMapSettings::RenderMapTile, flag );
274 }
275 
277 {
278  QList<QgsMapLayer *> layers = mapSettings().layers();
279  if ( index >= 0 && index < ( int ) layers.size() )
280  return layers[index];
281  else
282  return nullptr;
283 }
284 
285 
287 {
288  mCurrentLayer = layer;
289  emit currentLayerChanged( layer );
290 }
291 
292 double QgsMapCanvas::scale() const
293 {
294  return mapSettings().scale();
295 }
296 
298 {
299  return nullptr != mJob;
300 } // isDrawing
301 
302 // return the current coordinate transform based on the extents and
303 // device size
305 {
306  return &mapSettings().mapToPixel();
307 }
308 
309 void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
310 {
311  // following a theme => request denied!
312  if ( !mTheme.isEmpty() )
313  return;
314 
315  setLayersPrivate( layers );
316 }
317 
318 void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
319 {
320  QList<QgsMapLayer *> oldLayers = mSettings.layers();
321 
322  // update only if needed
323  if ( layers == oldLayers )
324  return;
325 
326  Q_FOREACH ( QgsMapLayer *layer, oldLayers )
327  {
328  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
329  disconnect( layer, &QgsMapLayer::crsChanged, this, &QgsMapCanvas::layerCrsChange );
330  disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
331  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
332  {
334  }
335  }
336 
337  mSettings.setLayers( layers );
338 
339  Q_FOREACH ( QgsMapLayer *layer, layers )
340  {
341  if ( !layer )
342  continue;
343  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
344  connect( layer, &QgsMapLayer::crsChanged, this, &QgsMapCanvas::layerCrsChange );
345  connect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
346  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
347  {
349  }
350  }
352 
353  QgsDebugMsg( "Layers have changed, refreshing" );
354  emit layersChanged();
355 
356  updateAutoRefreshTimer();
357  refresh();
358 }
359 
360 
362 {
363  return mSettings;
364 }
365 
367 {
368  if ( mSettings.destinationCrs() == crs )
369  return;
370 
371  // try to reproject current extent to the new one
372  QgsRectangle rect;
373  if ( !mSettings.visibleExtent().isEmpty() )
374  {
375  QgsCoordinateTransform transform( mSettings.destinationCrs(), crs );
376  try
377  {
378  rect = transform.transformBoundingBox( mSettings.visibleExtent() );
379  }
380  catch ( QgsCsException &e )
381  {
382  Q_UNUSED( e );
383  QgsDebugMsg( QString( "Transform error caught: %1" ).arg( e.what() ) );
384  }
385  }
386 
387  if ( !rect.isEmpty() )
388  {
389  setExtent( rect );
390  }
391 
392  mSettings.setDestinationCrs( crs );
393  updateScale();
394 
395  QgsDebugMsg( "refreshing after destination CRS changed" );
396  refresh();
397 
399 
400  emit destinationCrsChanged();
401 }
402 
403 void QgsMapCanvas::setMapSettingsFlags( QgsMapSettings::Flags flags )
404 {
405  mSettings.setFlags( flags );
406  clearCache();
407  refresh();
408 }
409 
411 {
412  return mLabelingResults;
413 }
414 
416 {
417  if ( enabled == isCachingEnabled() )
418  return;
419 
420  if ( mJob && mJob->isActive() )
421  {
422  // wait for the current rendering to finish, before touching the cache
423  mJob->waitForFinished();
424  }
425 
426  if ( enabled )
427  {
428  mCache = new QgsMapRendererCache;
429  }
430  else
431  {
432  delete mCache;
433  mCache = nullptr;
434  }
435 }
436 
438 {
439  return nullptr != mCache;
440 }
441 
443 {
444  if ( mCache )
445  mCache->clear();
446 }
447 
449 {
450  mUseParallelRendering = enabled;
451 }
452 
454 {
455  return mUseParallelRendering;
456 }
457 
458 void QgsMapCanvas::setMapUpdateInterval( int timeMilliseconds )
459 {
460  mMapUpdateTimer.setInterval( timeMilliseconds );
461 }
462 
464 {
465  return mMapUpdateTimer.interval();
466 }
467 
468 
470 {
471  return mCurrentLayer;
472 }
473 
474 
476 {
477  if ( !mSettings.hasValidSettings() )
478  {
479  QgsDebugMsg( "CANVAS refresh - invalid settings -> nothing to do" );
480  return;
481  }
482 
483  if ( !mRenderFlag || mFrozen )
484  {
485  QgsDebugMsg( "CANVAS render flag off" );
486  return;
487  }
488 
489  if ( mRefreshScheduled )
490  {
491  QgsDebugMsg( "CANVAS refresh already scheduled" );
492  return;
493  }
494 
495  mRefreshScheduled = true;
496 
497  QgsDebugMsg( "CANVAS refresh scheduling" );
498 
499  // schedule a refresh
500  mRefreshTimer->start( 1 );
501 } // refresh
502 
503 void QgsMapCanvas::refreshMap()
504 {
505  Q_ASSERT( mRefreshScheduled );
506 
507  QgsDebugMsgLevel( "CANVAS refresh!", 3 );
508 
509  stopRendering(); // if any...
510  stopPreviewJobs();
511 
512  //build the expression context
513  QgsExpressionContext expressionContext;
514  expressionContext << QgsExpressionContextUtils::globalScope()
517  << new QgsExpressionContextScope( mExpressionContextScope );
518 
519  mSettings.setExpressionContext( expressionContext );
520 
521  if ( !mTheme.isEmpty() )
522  {
523  // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
524  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
525  // current state of the style. If we had stored the style overrides earlier (such as in
526  // mapThemeChanged slot) then this xml could be out of date...
527  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
528  // just return the style name, we can instead set the overrides in mapThemeChanged and not here
529  mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
530  }
531 
532  // create the renderer job
533  Q_ASSERT( !mJob );
534  mJobCanceled = false;
535  if ( mUseParallelRendering )
536  mJob = new QgsMapRendererParallelJob( mSettings );
537  else
538  mJob = new QgsMapRendererSequentialJob( mSettings );
539  connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
540  mJob->setCache( mCache );
541 
542  mJob->start();
543 
544  // from now on we can accept refresh requests again
545  // this must be reset only after the job has been started, because
546  // some providers (yes, it's you WCS and AMS!) during preparation
547  // do network requests and start an internal event loop, which may
548  // end up calling refresh() and would schedule another refresh,
549  // deleting the one we have just started.
550  mRefreshScheduled = false;
551 
552  mMapUpdateTimer.start();
553 
554  emit renderStarting();
555 }
556 
557 void QgsMapCanvas::mapThemeChanged( const QString &theme )
558 {
559  if ( theme == mTheme )
560  {
561  // set the canvas layers to match the new layers contained in the map theme
562  // NOTE: we do this when the theme layers change and not when we are refreshing the map
563  // as setLayers() sets up necessary connections to handle changes to the layers
564  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
565  // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
566  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
567  // current state of the style. If changes were made to the style then this xml
568  // snapshot goes out of sync...
569  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
570  // just return the style name, we can instead set the overrides here and not in refreshMap()
571 
572  clearCache();
573  refresh();
574  }
575 }
576 
577 
578 void QgsMapCanvas::rendererJobFinished()
579 {
580  QgsDebugMsg( QString( "CANVAS finish! %1" ).arg( !mJobCanceled ) );
581 
582  mMapUpdateTimer.stop();
583 
584  // TODO: would be better to show the errors in message bar
585  Q_FOREACH ( const QgsMapRendererJob::Error &error, mJob->errors() )
586  {
587  QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) );
588  }
589 
590  if ( !mJobCanceled )
591  {
592  // take labeling results before emitting renderComplete, so labeling map tools
593  // connected to signal work with correct results
594  if ( !mJob->usedCachedLabels() )
595  {
596  delete mLabelingResults;
597  mLabelingResults = mJob->takeLabelingResults();
598  }
599 
600  QImage img = mJob->renderedImage();
601 
602  // emit renderComplete to get our decorations drawn
603  QPainter p( &img );
604  emit renderComplete( &p );
605 
606  QgsSettings settings;
607  if ( settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
608  {
609  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
610  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
611  }
612 
613  if ( mDrawRenderingStats )
614  {
615  int w = img.width(), h = img.height();
616  QFont fnt = p.font();
617  fnt.setBold( true );
618  p.setFont( fnt );
619  int lh = p.fontMetrics().height() * 2;
620  QRect r( 0, h - lh, w, lh );
621  p.setPen( Qt::NoPen );
622  p.setBrush( QColor( 0, 0, 0, 110 ) );
623  p.drawRect( r );
624  p.setPen( Qt::white );
625  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? "PARALLEL" : "SEQUENTIAL" ).arg( mJob->renderingTime() );
626  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
627  }
628 
629  p.end();
630 
631  mMap->setContent( img, imageRect( img, mSettings ) );
632  startPreviewJobs();
633  }
634 
635  // now we are in a slot called from mJob - do not delete it immediately
636  // so the class is still valid when the execution returns to the class
637  mJob->deleteLater();
638  mJob = nullptr;
639 
640  emit mapCanvasRefreshed();
641 }
642 
643 void QgsMapCanvas::previewJobFinished()
644 {
645  QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
646  Q_ASSERT( job );
647 
648  if ( mMap )
649  {
650  mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
651  mPreviewJobs.removeAll( job );
652  delete job;
653  }
654 }
655 
656 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
657 {
658  // This is a hack to pass QgsMapCanvasItem::setRect what it
659  // expects (encoding of position and size of the item)
660  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
661  QgsPointXY topLeft = m2p.toMapPoint( 0, 0 );
662  double res = m2p.mapUnitsPerPixel();
663  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
664  return rect;
665 }
666 
667 void QgsMapCanvas::mapUpdateTimeout()
668 {
669  if ( mJob )
670  {
671  const QImage &img = mJob->renderedImage();
672  mMap->setContent( img, imageRect( img, mSettings ) );
673  }
674 }
675 
677 {
678  if ( mJob )
679  {
680  QgsDebugMsg( "CANVAS stop rendering!" );
681  mJobCanceled = true;
682  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
683  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
684  mJob->cancelWithoutBlocking();
685  mJob = nullptr;
686  }
687 }
688 
689 //the format defaults to "PNG" if not specified
690 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
691 {
692  QPainter painter;
693  QImage image;
694 
695  //
696  //check if the optional QPaintDevice was supplied
697  //
698  if ( theQPixmap )
699  {
700  image = theQPixmap->toImage();
701  painter.begin( &image );
702 
703  // render
704  QgsMapRendererCustomPainterJob job( mSettings, &painter );
705  job.start();
706  job.waitForFinished();
707  emit renderComplete( &painter );
708  }
709  else //use the map view
710  {
711  image = mMap->contentImage().copy();
712  painter.begin( &image );
713  }
714 
715  // draw annotations
716  QStyleOptionGraphicsItem option;
717  option.initFrom( this );
718  QGraphicsItem *item = nullptr;
719  QListIterator<QGraphicsItem *> i( items() );
720  i.toBack();
721  while ( i.hasPrevious() )
722  {
723  item = i.previous();
724 
725  if ( !item || dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) )
726  {
727  continue;
728  }
729 
730  painter.save();
731 
732  QPointF itemScenePos = item->scenePos();
733  painter.translate( itemScenePos.x(), itemScenePos.y() );
734 
735  item->paint( &painter, &option );
736 
737  painter.restore();
738  }
739 
740  painter.end();
741  image.save( fileName, format.toLocal8Bit().data() );
742 
743  QFileInfo myInfo = QFileInfo( fileName );
744 
745  // build the world file name
746  QString outputSuffix = myInfo.suffix();
747  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
748  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
749  QFile myWorldFile( myWorldFileName );
750  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
751  {
752  return;
753  }
754  QTextStream myStream( &myWorldFile );
756 } // saveAsImage
757 
758 
759 
761 {
762  return mapSettings().visibleExtent();
763 } // extent
764 
766 {
767  return mapSettings().fullExtent();
768 } // extent
769 
770 
771 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
772 {
773  QgsRectangle current = extent();
774 
775  if ( ( r == current ) && magnified )
776  return;
777 
778  if ( r.isEmpty() )
779  {
780  if ( !mSettings.hasValidSettings() )
781  {
782  // we can't even just move the map center
783  QgsDebugMsg( "Empty extent - ignoring" );
784  return;
785  }
786 
787  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
788  QgsDebugMsg( "Empty extent - keeping old scale with new center!" );
789  setCenter( r.center() );
790  }
791  else
792  {
793  mSettings.setExtent( r, magnified );
794  }
795  emit extentsChanged();
796  updateScale();
797  if ( mLastExtent.size() > 20 )
798  mLastExtent.removeAt( 0 );
799 
800  //clear all extent items after current index
801  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
802  {
803  mLastExtent.removeAt( i );
804  }
805 
806  mLastExtent.append( extent() );
807 
808  // adjust history to no more than 20
809  if ( mLastExtent.size() > 20 )
810  {
811  mLastExtent.removeAt( 0 );
812  }
813 
814  // the last item is the current extent
815  mLastExtentIndex = mLastExtent.size() - 1;
816 
817  // update controls' enabled state
818  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
819  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
820 } // setExtent
821 
823 {
825  double x = center.x();
826  double y = center.y();
827  setExtent(
828  QgsRectangle(
829  x - r.width() / 2.0, y - r.height() / 2.0,
830  x + r.width() / 2.0, y + r.height() / 2.0
831  ),
832  true
833  );
834 } // setCenter
835 
837 {
839  return r.center();
840 }
841 
842 
844 {
845  return mapSettings().rotation();
846 } // rotation
847 
848 void QgsMapCanvas::setRotation( double degrees )
849 {
850  double current = rotation();
851 
852  if ( degrees == current )
853  return;
854 
855  mSettings.setRotation( degrees );
856  emit rotationChanged( degrees );
857  emit extentsChanged(); // visible extent changes with rotation
858 } // setRotation
859 
860 
862 {
863  emit scaleChanged( mapSettings().scale() );
864 }
865 
866 
868 {
870  // If the full extent is an empty set, don't do the zoom
871  if ( !extent.isEmpty() )
872  {
873  // Add a 5% margin around the full extent
874  extent.scale( 1.05 );
875  setExtent( extent );
876  }
877  refresh();
878 
879 } // zoomToFullExtent
880 
881 
882 
884 {
885  if ( mLastExtentIndex > 0 )
886  {
887  mLastExtentIndex--;
888  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
889  emit extentsChanged();
890  updateScale();
891  refresh();
892  // update controls' enabled state
893  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
894  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
895  }
896 
897 } // zoomToPreviousExtent
898 
900 {
901  if ( mLastExtentIndex < mLastExtent.size() - 1 )
902  {
903  mLastExtentIndex++;
904  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
905  emit extentsChanged();
906  updateScale();
907  refresh();
908  // update controls' enabled state
909  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
910  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
911  }
912 }// zoomToNextExtent
913 
915 {
916  mLastExtent.clear(); // clear the zoom history list
917  mLastExtent.append( extent() ) ; // set the current extent in the list
918  mLastExtentIndex = mLastExtent.size() - 1;
919  // update controls' enabled state
920  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
921  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
922 }// clearExtentHistory
923 
925 {
926  if ( !layer )
927  {
928  // use current layer by default
929  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
930  }
931 
932  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
933  return;
934 
935  QgsRectangle rect = layer->boundingBoxOfSelected();
936  if ( rect.isNull() )
937  {
938  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), QgsMessageBar::WARNING );
939  return;
940  }
941 
942  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
943  zoomToFeatureExtent( rect );
944 } // zoomToSelected
945 
947 {
948  // no selected features, only one selected point feature
949  //or two point features with the same x- or y-coordinates
950  if ( rect.isEmpty() )
951  {
952  // zoom in
953  QgsPointXY c = rect.center();
954  rect = extent();
955  rect.scale( 1.0, &c );
956  }
957  //zoom to an area
958  else
959  {
960  // Expand rect to give a bit of space around the selected
961  // objects so as to keep them clear of the map boundaries
962  // The same 5% should apply to all margins.
963  rect.scale( 1.05 );
964  }
965 
966  setExtent( rect );
967  refresh();
968 }
969 
971 {
972  if ( !layer )
973  {
974  return;
975  }
976 
977  QgsRectangle bbox;
978  QString errorMsg;
979  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
980  {
981  zoomToFeatureExtent( bbox );
982  }
983  else
984  {
985  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, QgsMessageBar::WARNING );
986  }
987 
988 }
989 
991 {
992  if ( !layer )
993  {
994  return;
995  }
996 
997  QgsRectangle bbox;
998  QString errorMsg;
999  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1000  {
1001  setCenter( bbox.center() );
1002  refresh();
1003  }
1004  else
1005  {
1006  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, QgsMessageBar::WARNING );
1007  }
1008 }
1009 
1010 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1011 {
1012  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setSubsetOfAttributes( QgsAttributeList() ) );
1013  bbox.setMinimal();
1014  QgsFeature fet;
1015  int featureCount = 0;
1016  errorMsg.clear();
1017 
1018  while ( it.nextFeature( fet ) )
1019  {
1020  QgsGeometry geom = fet.geometry();
1021  if ( geom.isNull() )
1022  {
1023  errorMsg = tr( "Feature does not have a geometry" );
1024  }
1025  else if ( geom.geometry()->isEmpty() )
1026  {
1027  errorMsg = tr( "Feature geometry is empty" );
1028  }
1029  if ( !errorMsg.isEmpty() )
1030  {
1031  return false;
1032  }
1034  bbox.combineExtentWith( r );
1035  featureCount++;
1036  }
1037 
1038  if ( featureCount != ids.count() )
1039  {
1040  errorMsg = tr( "Feature not found" );
1041  return false;
1042  }
1043 
1044  return true;
1045 }
1046 
1048 {
1049  if ( !layer )
1050  {
1051  // use current layer by default
1052  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1053  }
1054 
1055  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1056  return;
1057 
1058  QgsRectangle rect = layer->boundingBoxOfSelected();
1059  if ( rect.isNull() )
1060  {
1061  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), QgsMessageBar::WARNING );
1062  return;
1063  }
1064 
1065  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1066  setCenter( rect.center() );
1067  refresh();
1068 }
1069 
1070 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1071 {
1072  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1073  {
1074  emit keyPressed( e );
1075  return;
1076  }
1077 
1078  if ( ! mCanvasProperties->mouseButtonDown )
1079  {
1080  // Don't want to interfer with mouse events
1081 
1082  QgsRectangle currentExtent = mapSettings().visibleExtent();
1083  double dx = qAbs( currentExtent.width() / 4 );
1084  double dy = qAbs( currentExtent.height() / 4 );
1085 
1086  switch ( e->key() )
1087  {
1088  case Qt::Key_Left:
1089  QgsDebugMsg( "Pan left" );
1090  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1091  refresh();
1092  break;
1093 
1094  case Qt::Key_Right:
1095  QgsDebugMsg( "Pan right" );
1096  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1097  refresh();
1098  break;
1099 
1100  case Qt::Key_Up:
1101  QgsDebugMsg( "Pan up" );
1102  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1103  refresh();
1104  break;
1105 
1106  case Qt::Key_Down:
1107  QgsDebugMsg( "Pan down" );
1108  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1109  refresh();
1110  break;
1111 
1112 
1113 
1114  case Qt::Key_Space:
1115  QgsDebugMsg( "Pressing pan selector" );
1116 
1117  //mCanvasProperties->dragging = true;
1118  if ( ! e->isAutoRepeat() )
1119  {
1120  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1121  mCanvasProperties->panSelectorDown = true;
1122  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1123  }
1124  break;
1125 
1126  case Qt::Key_PageUp:
1127  QgsDebugMsg( "Zoom in" );
1128  zoomIn();
1129  break;
1130 
1131  case Qt::Key_PageDown:
1132  QgsDebugMsg( "Zoom out" );
1133  zoomOut();
1134  break;
1135 
1136 #if 0
1137  case Qt::Key_P:
1138  mUseParallelRendering = !mUseParallelRendering;
1139  refresh();
1140  break;
1141 
1142  case Qt::Key_S:
1143  mDrawRenderingStats = !mDrawRenderingStats;
1144  refresh();
1145  break;
1146 #endif
1147 
1148  default:
1149  // Pass it on
1150  if ( mMapTool )
1151  {
1152  mMapTool->keyPressEvent( e );
1153  }
1154  else e->ignore();
1155 
1156  QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
1157  }
1158  }
1159 
1160  emit keyPressed( e );
1161 
1162 } //keyPressEvent()
1163 
1164 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1165 {
1166  QgsDebugMsg( "keyRelease event" );
1167 
1168  switch ( e->key() )
1169  {
1170  case Qt::Key_Space:
1171  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1172  {
1173  QgsDebugMsg( "Releasing pan selector" );
1174  QApplication::restoreOverrideCursor();
1175  mCanvasProperties->panSelectorDown = false;
1176  panActionEnd( mCanvasProperties->mouseLastXY );
1177  }
1178  break;
1179 
1180  default:
1181  // Pass it on
1182  if ( mMapTool )
1183  {
1184  mMapTool->keyReleaseEvent( e );
1185  }
1186  else e->ignore();
1187 
1188  QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
1189  }
1190 
1191  emit keyReleased( e );
1192 
1193 } //keyReleaseEvent()
1194 
1195 
1197 {
1198  // call handler of current map tool
1199  if ( mMapTool )
1200  {
1201  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1202  mMapTool->canvasDoubleClickEvent( me.get() );
1203  }
1204 }// mouseDoubleClickEvent
1205 
1206 
1207 void QgsMapCanvas::beginZoomRect( QPoint pos )
1208 {
1209  mZoomRect.setRect( 0, 0, 0, 0 );
1210  QApplication::setOverrideCursor( mZoomCursor );
1211  mZoomDragging = true;
1212  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1213  QColor color( Qt::blue );
1214  color.setAlpha( 63 );
1215  mZoomRubberBand->setColor( color );
1216  mZoomRect.setTopLeft( pos );
1217 }
1218 
1219 void QgsMapCanvas::endZoomRect( QPoint pos )
1220 {
1221  mZoomDragging = false;
1222  mZoomRubberBand.reset( nullptr );
1223  QApplication::restoreOverrideCursor();
1224 
1225  // store the rectangle
1226  mZoomRect.setRight( pos.x() );
1227  mZoomRect.setBottom( pos.y() );
1228 
1229  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1230  {
1231  //probably a mistake - would result in huge zoom!
1232  return;
1233  }
1234 
1235  //account for bottom right -> top left dragging
1236  mZoomRect = mZoomRect.normalized();
1237 
1238  // set center and zoom
1239  const QSize &zoomRectSize = mZoomRect.size();
1240  const QSize &canvasSize = mSettings.outputSize();
1241  double sfx = ( double )zoomRectSize.width() / canvasSize.width();
1242  double sfy = ( double )zoomRectSize.height() / canvasSize.height();
1243  double sf = qMax( sfx, sfy );
1244 
1245  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1246 
1247  zoomByFactor( sf, &c );
1248  refresh();
1249 }
1250 
1251 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1252 {
1253  //use middle mouse button for panning, map tools won't receive any events in that case
1254  if ( e->button() == Qt::MidButton )
1255  {
1256  mCanvasProperties->panSelectorDown = true;
1257  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1258  }
1259  else
1260  {
1261  // call handler of current map tool
1262  if ( mMapTool )
1263  {
1264  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1265  && e->modifiers() & Qt::ShiftModifier )
1266  {
1267  beginZoomRect( e->pos() );
1268  return;
1269  }
1270  else
1271  {
1272  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1273  mMapTool->canvasPressEvent( me.get() );
1274  }
1275  }
1276  }
1277 
1278  if ( mCanvasProperties->panSelectorDown )
1279  {
1280  return;
1281  }
1282 
1283  mCanvasProperties->mouseButtonDown = true;
1284  mCanvasProperties->rubberStartPoint = e->pos();
1285 
1286 } // mousePressEvent
1287 
1288 
1289 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1290 {
1291  //use middle mouse button for panning, map tools won't receive any events in that case
1292  if ( e->button() == Qt::MidButton )
1293  {
1294  mCanvasProperties->panSelectorDown = false;
1295  panActionEnd( mCanvasProperties->mouseLastXY );
1296  }
1297  else if ( e->button() == Qt::BackButton )
1298  {
1300  return;
1301  }
1302  else if ( e->button() == Qt::ForwardButton )
1303  {
1304  zoomToNextExtent();
1305  return;
1306  }
1307  else
1308  {
1309  if ( mZoomDragging && e->button() == Qt::LeftButton )
1310  {
1311  endZoomRect( e->pos() );
1312  return;
1313  }
1314 
1315  // call handler of current map tool
1316  if ( mMapTool )
1317  {
1318  // right button was pressed in zoom tool? return to previous non zoom tool
1319  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1320  {
1321  QgsDebugMsg( "Right click in map tool zoom or pan, last tool is " +
1322  QString( mLastNonZoomMapTool ? "not null." : "null." ) );
1323 
1324  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1325 
1326  // change to older non-zoom tool
1327  if ( mLastNonZoomMapTool
1328  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1329  || ( vlayer && vlayer->isEditable() ) ) )
1330  {
1331  QgsMapTool *t = mLastNonZoomMapTool;
1332  mLastNonZoomMapTool = nullptr;
1333  setMapTool( t );
1334  }
1335  return;
1336  }
1337  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1338  mMapTool->canvasReleaseEvent( me.get() );
1339  }
1340  }
1341 
1342 
1343  mCanvasProperties->mouseButtonDown = false;
1344 
1345  if ( mCanvasProperties->panSelectorDown )
1346  return;
1347 
1348 } // mouseReleaseEvent
1349 
1350 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1351 {
1352  QGraphicsView::resizeEvent( e );
1353  mResizeTimer->start( 500 );
1354 
1355  QSize lastSize = viewport()->size();
1356 
1357  mSettings.setOutputSize( lastSize );
1358 
1359  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1360 
1361  moveCanvasContents( true );
1362 
1363  updateScale();
1364 
1365  //refresh();
1366 
1367  emit extentsChanged();
1368 }
1369 
1370 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1371 {
1372  // no custom event handling anymore
1373 
1374  QGraphicsView::paintEvent( e );
1375 } // paintEvent
1376 
1378 {
1379  QList<QGraphicsItem *> list = mScene->items();
1380  QList<QGraphicsItem *>::iterator it = list.begin();
1381  while ( it != list.end() )
1382  {
1383  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( *it );
1384 
1385  if ( item )
1386  {
1387  item->updatePosition();
1388  }
1389 
1390  ++it;
1391  }
1392 }
1393 
1394 
1395 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1396 {
1397  // Zoom the map canvas in response to a mouse wheel event. Moving the
1398  // wheel forward (away) from the user zooms in
1399 
1400  QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
1401 
1402  if ( mMapTool )
1403  {
1404  mMapTool->wheelEvent( e );
1405  if ( e->isAccepted() )
1406  return;
1407  }
1408 
1409  double zoomFactor = mWheelZoomFactor;
1410 
1411  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1412  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * qAbs( e->angleDelta().y() );
1413 
1414  if ( e->modifiers() & Qt::ControlModifier )
1415  {
1416  //holding ctrl while wheel zooming results in a finer zoom
1417  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1418  }
1419 
1420  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1421 
1422  // zoom map to mouse cursor by scaling
1423  QgsPointXY oldCenter = center();
1424  QgsPointXY mousePos( getCoordinateTransform()->toMapPoint( e->x(), e->y() ) );
1425  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1426  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1427 
1428  zoomByFactor( signedWheelFactor, &newCenter );
1429 }
1430 
1431 void QgsMapCanvas::setWheelFactor( double factor )
1432 {
1433  mWheelZoomFactor = factor;
1434 }
1435 
1437 {
1438  // magnification is alreday handled in zoomByFactor
1439  zoomByFactor( 1 / mWheelZoomFactor );
1440 }
1441 
1443 {
1444  // magnification is alreday handled in zoomByFactor
1445  zoomByFactor( mWheelZoomFactor );
1446 }
1447 
1448 void QgsMapCanvas::zoomScale( double newScale )
1449 {
1450  zoomByFactor( newScale / scale() );
1451 }
1452 
1453 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1454 {
1455  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1456 
1457  if ( mScaleLocked )
1458  {
1460  }
1461  else
1462  {
1463  // transform the mouse pos to map coordinates
1466  r.scale( scaleFactor, &center );
1467  setExtent( r, true );
1468  refresh();
1469  }
1470 }
1471 
1472 void QgsMapCanvas::setScaleLocked( bool isLocked )
1473 {
1474  mScaleLocked = isLocked;
1475 }
1476 
1477 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1478 {
1479  mCanvasProperties->mouseLastXY = e->pos();
1480 
1481  if ( mCanvasProperties->panSelectorDown )
1482  {
1483  panAction( e );
1484  }
1485  else if ( mZoomDragging )
1486  {
1487  mZoomRect.setBottomRight( e->pos() );
1488  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1489  mZoomRubberBand->show();
1490  }
1491  else
1492  {
1493  // call handler of current map tool
1494  if ( mMapTool )
1495  {
1496  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1497  mMapTool->canvasMoveEvent( me.get() );
1498  }
1499  }
1500 
1501  // show x y on status bar
1502  QPoint xy = e->pos();
1504  emit xyCoordinates( coord );
1505 }
1506 
1508 {
1509  if ( !tool )
1510  return;
1511 
1512  if ( mMapTool )
1513  {
1514  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1515  mMapTool->deactivate();
1516  }
1517 
1518  if ( ( tool->flags() & QgsMapTool::Transient )
1519  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1520  {
1521  // if zoom or pan tool will be active, save old tool
1522  // to bring it back on right click
1523  // (but only if it wasn't also zoom or pan tool)
1524  mLastNonZoomMapTool = mMapTool;
1525  }
1526  else
1527  {
1528  mLastNonZoomMapTool = nullptr;
1529  }
1530 
1531  QgsMapTool *oldTool = mMapTool;
1532 
1533  // set new map tool and activate it
1534  mMapTool = tool;
1535  if ( mMapTool )
1536  {
1537  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1538  mMapTool->activate();
1539  }
1540 
1541  emit mapToolSet( mMapTool, oldTool );
1542 } // setMapTool
1543 
1545 {
1546  if ( mMapTool && mMapTool == tool )
1547  {
1548  mMapTool->deactivate();
1549  mMapTool = nullptr;
1550  emit mapToolSet( nullptr, mMapTool );
1551  setCursor( Qt::ArrowCursor );
1552  }
1553 
1554  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1555  {
1556  mLastNonZoomMapTool = nullptr;
1557  }
1558 }
1559 
1560 void QgsMapCanvas::setCanvasColor( const QColor &color )
1561 {
1562  // background of map's pixmap
1563  mSettings.setBackgroundColor( color );
1564 
1565  // background of the QGraphicsView
1566  QBrush bgBrush( color );
1567  setBackgroundBrush( bgBrush );
1568 #if 0
1569  QPalette palette;
1570  palette.setColor( backgroundRole(), color );
1571  setPalette( palette );
1572 #endif
1573 
1574  // background of QGraphicsScene
1575  mScene->setBackgroundBrush( bgBrush );
1576 }
1577 
1579 {
1580  return mScene->backgroundBrush().color();
1581 }
1582 
1583 void QgsMapCanvas::setSelectionColor( const QColor &color )
1584 {
1585  mSettings.setSelectionColor( color );
1586 }
1587 
1589 {
1590  return mapSettings().layers().size();
1591 } // layerCount
1592 
1593 
1594 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1595 {
1596  return mapSettings().layers();
1597 }
1598 
1599 
1601 {
1602  // called when a layer has changed visibility setting
1603 
1604  refresh();
1605 
1606 } // layerStateChange
1607 
1609 {
1610  // called when a layer's CRS has been changed
1611  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
1612  QString destAuthId = mSettings.destinationCrs().authid();
1613  getDatumTransformInfo( layer, layer->crs().authid(), destAuthId );
1614 
1615 } // layerCrsChange
1616 
1617 
1618 void QgsMapCanvas::freeze( bool frozen )
1619 {
1620  mFrozen = frozen;
1621 }
1622 
1624 {
1625  return mFrozen;
1626 }
1627 
1628 
1630 {
1631  return mapSettings().mapUnitsPerPixel();
1632 } // mapUnitsPerPixel
1633 
1635 {
1636  return mapSettings().mapUnits();
1637 }
1638 
1639 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1640 {
1641  return mSettings.layerStyleOverrides();
1642 }
1643 
1644 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1645 {
1646  if ( overrides == mSettings.layerStyleOverrides() )
1647  return;
1648 
1649  mSettings.setLayerStyleOverrides( overrides );
1650  clearCache();
1652 }
1653 
1654 void QgsMapCanvas::setTheme( const QString &theme )
1655 {
1656  if ( mTheme == theme )
1657  return;
1658 
1659  clearCache();
1660  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1661  {
1662  mTheme.clear();
1663  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1664  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1665  emit themeChanged( QString() );
1666  }
1667  else
1668  {
1669  mTheme = theme;
1670  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1671  emit themeChanged( theme );
1672  }
1673 }
1674 
1676 {
1677  mRenderFlag = flag;
1678 
1679  if ( mRenderFlag )
1680  {
1681  refresh();
1682  }
1683  else
1684  stopRendering();
1685 }
1686 
1687 #if 0
1688 void QgsMapCanvas::connectNotify( const char *signal )
1689 {
1690  Q_UNUSED( signal );
1691  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1692 } //connectNotify
1693 #endif
1694 
1696 {
1697  QString destAuthId = mSettings.destinationCrs().authid();
1698  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1699  {
1700  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
1701  if ( vl && vl->geometryType() == QgsWkbTypes::NullGeometry )
1702  continue;
1703 
1704  // if there are more options, ask the user which datum transform to use
1705  if ( !mSettings.datumTransformStore().hasEntryForLayer( layer ) )
1706  getDatumTransformInfo( layer, layer->crs().authid(), destAuthId );
1707  }
1708 }
1709 
1710 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1711 {
1712  if ( !deferred )
1713  refresh();
1714 }
1715 
1716 void QgsMapCanvas::autoRefreshTriggered()
1717 {
1718  if ( mJob )
1719  {
1720  // canvas is currently being redrawn, so we skip this auto refresh
1721  // otherwise we could get stuck in the situation where an auto refresh is triggered
1722  // too often to allow the canvas to ever finish rendering
1723  return;
1724  }
1725 
1726  refresh();
1727 }
1728 
1729 void QgsMapCanvas::updateAutoRefreshTimer()
1730 {
1731  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1732  // trigger a map refresh on this minimum interval
1733  int minAutoRefreshInterval = -1;
1734  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1735  {
1736  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1737  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? qMin( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1738  }
1739 
1740  if ( minAutoRefreshInterval > 0 )
1741  {
1742  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1743  mAutoRefreshTimer.start();
1744  }
1745  else
1746  {
1747  mAutoRefreshTimer.stop();
1748  }
1749 }
1750 
1751 void QgsMapCanvas::projectThemesChanged()
1752 {
1753  if ( mTheme.isEmpty() )
1754  return;
1755 
1756  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1757  {
1758  // theme has been removed - stop following
1759  setTheme( QString() );
1760  }
1761 
1762 }
1763 
1765 {
1766  return mMapTool;
1767 }
1768 
1769 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
1770 {
1771  // move map image and other items to standard position
1772  moveCanvasContents( true ); // true means reset
1773 
1774  // use start and end box points to calculate the extent
1775  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
1777 
1778  // modify the center
1779  double dx = end.x() - start.x();
1780  double dy = end.y() - start.y();
1781  QgsPointXY c = center();
1782  c.set( c.x() - dx, c.y() - dy );
1783  setCenter( c );
1784 
1785  refresh();
1786 }
1787 
1788 void QgsMapCanvas::panAction( QMouseEvent *e )
1789 {
1790  Q_UNUSED( e );
1791 
1792  // move all map canvas items
1794 }
1795 
1797 {
1798  QPoint pnt( 0, 0 );
1799  if ( !reset )
1800  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
1801 
1802  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
1803 }
1804 
1806 {
1807  return mCanvasProperties->mouseLastXY;
1808 }
1809 
1810 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
1811 {
1812  if ( !mPreviewEffect )
1813  {
1814  return;
1815  }
1816 
1817  mPreviewEffect->setEnabled( previewEnabled );
1818 }
1819 
1821 {
1822  if ( !mPreviewEffect )
1823  {
1824  return false;
1825  }
1826 
1827  return mPreviewEffect->isEnabled();
1828 }
1829 
1831 {
1832  if ( !mPreviewEffect )
1833  {
1834  return;
1835  }
1836 
1837  mPreviewEffect->setMode( mode );
1838 }
1839 
1841 {
1842  if ( !mPreviewEffect )
1843  {
1845  }
1846 
1847  return mPreviewEffect->mode();
1848 }
1849 
1851 {
1852  if ( !mSnappingUtils )
1853  {
1854  // associate a dummy instance, but better than null pointer
1855  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
1856  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
1857  }
1858  return mSnappingUtils;
1859 }
1860 
1862 {
1863  mSnappingUtils = utils;
1864 }
1865 
1866 void QgsMapCanvas::readProject( const QDomDocument &doc )
1867 {
1868  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
1869  if ( nodes.count() )
1870  {
1871  QDomNode node = nodes.item( 0 );
1872 
1873  // Search the specific MapCanvas node using the name
1874  if ( nodes.count() > 1 )
1875  {
1876  for ( int i = 0; i < nodes.size(); ++i )
1877  {
1878  QDomElement elementNode = nodes.at( i ).toElement();
1879 
1880  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
1881  {
1882  node = nodes.at( i );
1883  break;
1884  }
1885  }
1886  }
1887 
1888  QgsMapSettings tmpSettings;
1889  tmpSettings.readXml( node );
1890  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
1891  {
1892  // never manually set the crs for the main canvas - this is instead connected to the project CRS
1893  setDestinationCrs( tmpSettings.destinationCrs() );
1894  }
1895  setExtent( tmpSettings.extent() );
1896  setRotation( tmpSettings.rotation() );
1897  mSettings.datumTransformStore() = tmpSettings.datumTransformStore();
1899 
1900  clearExtentHistory(); // clear the extent history on project load
1901 
1902  QDomElement elem = node.toElement();
1903  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
1904  {
1905  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
1906  {
1907  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
1908  }
1909  }
1910  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
1911  }
1912  else
1913  {
1914  QgsDebugMsg( "Couldn't read mapcanvas information from project" );
1915  }
1916 }
1917 
1918 void QgsMapCanvas::writeProject( QDomDocument &doc )
1919 {
1920  // create node "mapcanvas" and call mMapRenderer->writeXml()
1921 
1922  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1923  if ( !nl.count() )
1924  {
1925  QgsDebugMsg( "Unable to find qgis element in project file" );
1926  return;
1927  }
1928  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1929 
1930  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
1931  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
1932  if ( !mTheme.isEmpty() )
1933  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
1934  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
1935  qgisNode.appendChild( mapcanvasNode );
1936 
1937  mSettings.writeXml( mapcanvasNode, doc );
1938  // TODO: store only units, extent, projections, dest CRS
1939 }
1940 
1941 void QgsMapCanvas::getDatumTransformInfo( const QgsMapLayer *ml, const QString &srcAuthId, const QString &destAuthId )
1942 {
1943  if ( !ml )
1944  {
1945  return;
1946  }
1947 
1948  //check if default datum transformation available
1949  QgsSettings s;
1950  QString settingsString = "/Projections/" + srcAuthId + "//" + destAuthId;
1951  QVariant defaultSrcTransform = s.value( settingsString + "_srcTransform" );
1952  QVariant defaultDestTransform = s.value( settingsString + "_destTransform" );
1953  if ( defaultSrcTransform.isValid() && defaultDestTransform.isValid() )
1954  {
1955  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, defaultSrcTransform.toInt(), defaultDestTransform.toInt() );
1956  return;
1957  }
1958 
1961 
1962  if ( !s.value( QStringLiteral( "/Projections/showDatumTransformDialog" ), false ).toBool() )
1963  {
1964  // just use the default transform
1965  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, -1, -1 );
1966  return;
1967  }
1968 
1969  //get list of datum transforms
1970  QList< QList< int > > dt = QgsCoordinateTransform::datumTransformations( srcCRS, destCRS );
1971  if ( dt.size() < 2 )
1972  {
1973  return;
1974  }
1975 
1976  //if several possibilities: present dialog
1977  QgsDatumTransformDialog d( ml->name(), dt );
1978  d.setDatumTransformInfo( srcCRS.authid(), destCRS.authid() );
1979  if ( d.exec() == QDialog::Accepted )
1980  {
1981  int srcTransform = -1;
1982  int destTransform = -1;
1983  QList<int> t = d.selectedDatumTransform();
1984  if ( !t.isEmpty() )
1985  {
1986  srcTransform = t.at( 0 );
1987  }
1988  if ( t.size() > 1 )
1989  {
1990  destTransform = t.at( 1 );
1991  }
1992  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, srcTransform, destTransform );
1993  if ( d.rememberSelection() )
1994  {
1995  s.setValue( settingsString + "_srcTransform", srcTransform );
1996  s.setValue( settingsString + "_destTransform", destTransform );
1997  }
1998  }
1999  else
2000  {
2001  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, -1, -1 );
2002  }
2003 }
2004 
2005 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center )
2006 {
2007  if ( mScaleLocked )
2008  {
2009  // zoom map to mouse cursor by magnifying
2011  }
2012  else
2013  {
2015  r.scale( scaleFactor, center );
2016  setExtent( r, true );
2017  refresh();
2018  }
2019 }
2020 
2022 {
2023  // Find out which layer it was that sent the signal.
2024  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
2025  emit selectionChanged( layer );
2026  refresh();
2027 }
2028 
2029 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *e )
2030 {
2031  // By default graphics view delegates the drag events to graphics items.
2032  // But we do not want that and by ignoring the drag enter we let the
2033  // parent (e.g. QgisApp) to handle drops of map layers etc.
2034  e->ignore();
2035 }
2036 
2037 void QgsMapCanvas::mapToolDestroyed()
2038 {
2039  QgsDebugMsg( "maptool destroyed" );
2040  mMapTool = nullptr;
2041 }
2042 
2043 bool QgsMapCanvas::event( QEvent *e )
2044 {
2045  if ( !QTouchDevice::devices().empty() )
2046  {
2047  if ( e->type() == QEvent::Gesture )
2048  {
2049  // call handler of current map tool
2050  if ( mMapTool )
2051  {
2052  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2053  }
2054  }
2055  }
2056 
2057  // pass other events to base class
2058  return QGraphicsView::event( e );
2059 }
2060 
2062 {
2063  // reload all layers in canvas
2064  for ( int i = 0; i < layerCount(); i++ )
2065  {
2066  QgsMapLayer *l = layer( i );
2067  if ( l )
2068  l->reload();
2069  }
2070 
2071  // clear the cache
2072  clearCache();
2073 
2074  // and then refresh
2075  refresh();
2076 }
2077 
2079 {
2080  while ( mRefreshScheduled || mJob )
2081  {
2082  QgsApplication::processEvents();
2083  }
2084 }
2085 
2087 {
2088  mSettings.setSegmentationTolerance( tolerance );
2089 }
2090 
2092 {
2093  mSettings.setSegmentationToleranceType( type );
2094 }
2095 
2096 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2097 {
2098  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2099  QList<QGraphicsItem *> itemList = mScene->items();
2100  QList<QGraphicsItem *>::iterator it = itemList.begin();
2101  for ( ; it != itemList.end(); ++it )
2102  {
2103  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( *it );
2104  if ( aItem )
2105  {
2106  annotationItemList.push_back( aItem );
2107  }
2108  }
2109 
2110  return annotationItemList;
2111 }
2112 
2114 {
2115  mAnnotationsVisible = show;
2116  Q_FOREACH ( QgsMapCanvasAnnotationItem *item, annotationItems() )
2117  {
2118  item->setVisible( show );
2119  }
2120 }
2121 
2123 {
2124  mSettings.setLabelingEngineSettings( settings );
2125 }
2126 
2128 {
2129  return mSettings.labelingEngineSettings();
2130 }
2131 
2132 void QgsMapCanvas::startPreviewJobs()
2133 {
2134  stopPreviewJobs(); //just in case still running
2135 
2136  QgsRectangle mapRect = mSettings.visibleExtent();
2137 
2138  for ( int j = 0; j < 3; ++j )
2139  {
2140  for ( int i = 0; i < 3; ++i )
2141  {
2142  if ( i == 1 && j == 1 )
2143  {
2144  continue;
2145  }
2146 
2147 
2148  //copy settings, only update extent
2149  QgsMapSettings jobSettings = mSettings;
2150 
2151  double dx = ( i - 1 ) * mapRect.width();
2152  double dy = ( 1 - j ) * mapRect.height();
2153  QgsRectangle jobExtent = mapRect;
2154  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
2155  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
2156  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
2157  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
2158 
2159  jobSettings.setExtent( jobExtent );
2160  jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
2161 
2162  QgsMapRendererQImageJob *job = new QgsMapRendererParallelJob( jobSettings );
2163  mPreviewJobs.append( job );
2164  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2165  job->start();
2166  }
2167  }
2168 }
2169 
2170 void QgsMapCanvas::stopPreviewJobs()
2171 {
2172  QList< QgsMapRendererQImageJob * >::const_iterator it = mPreviewJobs.constBegin();
2173  for ( ; it != mPreviewJobs.constEnd(); ++it )
2174  {
2175  if ( *it )
2176  {
2177  disconnect( *it, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2178  connect( *it, &QgsMapRendererQImageJob::finished, *it, &QgsMapRendererQImageJob::deleteLater );
2179  ( *it )->cancelWithoutBlocking();
2180  }
2181  }
2182  mPreviewJobs.clear();
2183 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
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:59
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:114
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:38
Base class for all map layer types.
Definition: qgsmaplayer.h:54
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:723
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:146
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.
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.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
static QList< QList< int > > datumTransformations(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destinationCrs)
Returns list of datum transformations for the given src and dest CRS.
QList< QgsMapLayer * > layers() const
Return the list of layers shown within the map canvas.
const char * zoom_in[]
Bitmap cursors for map operations.
Definition: qgscursors.cpp:23
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
double magnificationFactor() const
Return 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:70
void clearExtentHistory()
This class is a composition of two QSettings instances:
Definition: qgssettings.h:54
bool event(QEvent *e) override
Overridden standard event to be gestures aware.
bool mouseButtonDown
Flag to indicate status of mouse button.
void wheelEvent(QWheelEvent *e) override
Overridden mouse wheel event.
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:519
void stopRendering()
stop rendering (if there is any right now)
double y
Definition: qgspointxy.h:47
A class to represent a 2D point.
Definition: qgspointxy.h:42
double rotation() const
Get 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.
int selectedFeatureCount() const
The number of features that are selected in this layer.
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:347
Allow zooming by rectangle (by holding shift and dragging) while the tool is active.
Definition: qgsmaptool.h:92
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
return number of layers on the map
const QgsDatumTransformStore & datumTransformStore() const
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
void crsChanged()
Emit a signal that layer&#39;s CRS has been reset.
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)
Set 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:68
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:96
void renderComplete(QPainter *)
Emitted when the canvas has rendered.
Snapping utils instance that is connected to a canvas and updates the configuration (map settings + c...
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QgsMapTool * mapTool()
Returns the currently active tool.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
QList< QgsMapLayer * > layers() const
Get list of layers for map rendering The layers are stored in the reverse order of how they are rende...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
void mousePressEvent(QMouseEvent *e) override
Overridden mouse press event.
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:99
void mapThemesChanged()
Emitted when map themes within the collection are changed.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
virtual QImage renderedImage()=0
Get a preview/resulting image.
int renderingTime() const
Find out how long 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
Return the actual extent derived from requested extent that takes takes output image size into accoun...
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:203
Enable drawing of labels on top of the map.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
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:156
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
Get 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 resizeEvent(QResizeEvent *e) override
Overridden resize event.
void setDatumTransformInfo(const QString &srcCRSauthId, const QString &destCRSauthId)
Deprecated to be deleted, stuff from here should be moved elsewhere.
void setMapTool(QgsMapTool *mapTool)
Sets the map tool currently being used on the canvas.
void enableMapTileRendering(bool flag)
sets map tile rendering flag
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:84
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:35
void setSnappingUtils(QgsSnappingUtils *utils)
Assign an instance of snapping utils to the map canvas.
virtual void keyReleaseEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:176
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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
Return geographical coordinates of the rectangle that should be rendered.
Map tool is an edit tool, which can only be used when layer is editable.
Definition: qgsmaptool.h:91
void setOutputSize(QSize size)
Set 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.
virtual 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:86
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global labeling engine settings in the internal map settings.
void addEntry(const QString &layerId, const QString &srcAuthId, const QString &destAuthId, int srcDatumTransform, int destDatumTransform)
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:38
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.
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled) ...
void layerCrsChange()
This slot is connected to the layer&#39;s CRS change.
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:36
double scale() const
Returns the calculated map scale.
Job implementation that renders all layers in parallel.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect)
This signal is emitted when selection was changed.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:118
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:75
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.
QgsMapCanvas(QWidget *parent=0)
Constructor.
virtual void waitForFinished()=0
Block until the job has finished.
double mapUnitsPerPixel() const
Return the distance in geographical coordinates that equals to one pixel in the map.
void readProject(const QDomDocument &)
emitted when project is being read
Enable anti-aliasing for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void getDatumTransformInfo(const QgsMapLayer *ml, const QString &srcAuthId, const QString &destAuthId)
ask user about datum transformation
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Centers canvas extent to feature ids.
#define M_PI
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.
virtual bool isSpatial() const override
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
void setWheelFactor(double factor)
set wheel zoom factor (should be greater than 1)
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
void messageEmitted(const QString &title, const QString &message, QgsMessageBar::MessageLevel=QgsMessageBar::INFO)
emit a message (usually to be displayed in a message bar)
void destinationCrsChanged()
Emitted when map CRS has changed.
void updateDatumTransformEntries()
Make sure the datum transform store is properly populated.
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:100
Single scope for storing variables and functions for use within a QgsExpressionContext.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
double mapUnitsPerPixel() const
Return 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:166
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.
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:60
void zoomToPreviousExtent()
Zoom to the previous extent (view)
bool isDrawing()
Find out whether rendering is in progress.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
double x
Definition: qgspointxy.h:46
void zoomToSelected(QgsVectorLayer *layer=nullptr)
Zoom to the extent of the selected features of current (vector) layer.
A class to represent a vector.
Definition: qgsvector.h:26
PreviewMode mode() const
Returns the mode used for the preview effect.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
virtual void start()=0
Start the rendering job and immediately return.
QPoint mouseLastXY()
returns last position of mouse cursor
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:106
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.
void selectionChanged(QgsMapLayer *layer)
Emitted when selection in any layer gets changed.
virtual void keyPressEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:171
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:42
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:91
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
QColor canvasColor() const
Read property of QColor bgColor.
void clear()
Invalidates the cache contents, clearing all cached images.
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.
QgsUnitTypes::DistanceUnit mapUnits() const
Convience function for returning the current canvas map units.
void setBackgroundColor(const QColor &color)
Set the background color of the map.
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global configuration of the labeling engine.
void mouseReleaseEvent(QMouseEvent *e) override
Overridden mouse release event.
void writeProject(QDomDocument &)
emitted when project is being written
QgsPointXY toMapPoint(double x, double y) const
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:180
QMap< QString, QString > layerStyleOverrides() const
Get map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
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...
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
QString theme() const
Returns the map&#39;s theme shown in the canvas, if set.
Definition: qgsmapcanvas.h:357
QgsPointXY center() const
Get map center, in geographical coordinates.
virtual bool gestureEvent(QGestureEvent *e)
gesture event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:181
void setSelectionColor(const QColor &color)
Set 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:151
bool hasEntryForLayer(QgsMapLayer *layer) const
void dragEnterEvent(QDragEnterEvent *e) override
Overridden drag enter event.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:80
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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:377
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
Get access to the labeling results (may be null)
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
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.
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)
Set the extent of the map canvas.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:96
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
const QgsMapToPixel * getCoordinateTransform()
Get the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
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:101
void setSelectionColor(const QColor &color)
Set color of selected vector features.
QString name
Definition: qgsmaplayer.h:58
void paintEvent(QPaintEvent *e) override
Overridden paint event.
void layerStateChange()
This slot is connected to the visibility change of one or more layers.
Enable vector simplification and other rendering optimizations.
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.
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.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:64
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:146
QList< int > QgsAttributeList
Definition: qgsfield.h:27
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
Return snapping utility class that is associated with map canvas.
virtual QgsLabelingResults * takeLabelingResults()=0
Get 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.
virtual 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
Return map settings with which this job was started.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
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.
QgsAbstractGeometry * geometry() const
Returns the underlying geometry store.
QSize outputSize() const
Return 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
QgsMapLayer * layer(int index)
return the map layer at position index in the layer stack
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:65
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:125
void setMagnificationFactor(double factor)
Sets the factor of magnification to apply to the map canvas.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns 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.
virtual bool usedCachedLabels() const =0
Returns true if the render job was able to use a cached labeling solution.
virtual void canvasReleaseEvent(QgsMapMouseEvent *e)
Mouse release event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:161