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