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