QGIS API Documentation  2.99.0-Master (9caa722)
qgsmapcanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 qgsmapcanvas.cpp - description
3 ------------------ -
4 begin : Sun Jun 30 2002
5 copyright : ( C ) 2002 by Gary E.Sherman
6 email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 #include <QtGlobal>
20 #include <QApplication>
21 #include <QCursor>
22 #include <QDir>
23 #include <QFile>
24 #include <QGraphicsItem>
25 #include <QGraphicsScene>
26 #include <QGraphicsView>
27 #include <QKeyEvent>
28 #include <QMouseEvent>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QPixmap>
32 #include <QRect>
33 #include <QTextStream>
34 #include <QResizeEvent>
35 #include <QString>
36 #include <QStringList>
37 #include <QWheelEvent>
38 
39 #include "qgis.h"
40 #include "qgssettings.h"
42 #include "qgsapplication.h"
43 #include "qgsexception.h"
45 #include "qgsfeatureiterator.h"
46 #include "qgslogger.h"
47 #include "qgsmapcanvas.h"
48 #include "qgsmapcanvasmap.h"
50 #include "qgsmaplayer.h"
51 #include "qgsmaptoolpan.h"
52 #include "qgsmaptoolzoom.h"
53 #include "qgsmaptopixel.h"
54 #include "qgsmapoverviewcanvas.h"
55 #include "qgsmaprenderercache.h"
59 #include "qgsmapsettingsutils.h"
60 #include "qgsmessagelog.h"
61 #include "qgsmessageviewer.h"
62 #include "qgspallabeling.h"
63 #include "qgsproject.h"
64 #include "qgsrubberband.h"
65 #include "qgsvectorlayer.h"
66 #include "qgscursors.h"
67 #include "qgsmapthemecollection.h"
68 #include <cmath>
69 
70 
75 //TODO QGIS 3.0 - remove
77 {
78  public:
79 
83  CanvasProperties() = default;
84 
86  bool mouseButtonDown{ false };
87 
89  QPoint mouseLastXY;
90 
93 
95  bool panSelectorDown{ false };
96 };
97 
98 
99 
100 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
101  : QGraphicsView( parent )
103  , mFrozen( false )
104  , mRefreshScheduled( false )
105  , mRenderFlag( true ) // by default, the canvas is rendered
106  , mLastExtentIndex( -1 )
107  , mWheelZoomFactor( 2.0 )
108  , mJobCanceled( false )
109  , mUseParallelRendering( false )
110  , mDrawRenderingStats( false )
111  , mScaleLocked( false )
112  , mExpressionContextScope( tr( "Map Canvas" ) )
113  , mZoomDragging( false )
114 {
115  mScene = new QGraphicsScene();
116  setScene( mScene );
117  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
118  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
119  setMouseTracking( true );
120  setFocusPolicy( Qt::StrongFocus );
121 
122  mResizeTimer = new QTimer( this );
123  mResizeTimer->setSingleShot( true );
124  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
125 
126  mRefreshTimer = new QTimer( this );
127  mRefreshTimer->setSingleShot( true );
128  connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
129 
130  // create map canvas item which will show the map
131  mMap = new QgsMapCanvasMap( this );
132 
133  // project handling
135  this, &QgsMapCanvas::readProject );
138 
139  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
140  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
141 
145  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
147  this, [ = ]
148  {
149  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
150  } );
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  mRefreshTimer->start( 1 );
491 } // refresh
492 
493 void QgsMapCanvas::refreshMap()
494 {
495  Q_ASSERT( mRefreshScheduled );
496 
497  QgsDebugMsgLevel( "CANVAS refresh!", 3 );
498 
499  stopRendering(); // if any...
500  stopPreviewJobs();
501 
502  //build the expression context
503  QgsExpressionContext expressionContext;
504  expressionContext << QgsExpressionContextUtils::globalScope()
507  << new QgsExpressionContextScope( mExpressionContextScope );
508 
509  mSettings.setExpressionContext( expressionContext );
510 
511  if ( !mTheme.isEmpty() )
512  {
513  // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
514  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
515  // current state of the style. If we had stored the style overrides earlier (such as in
516  // mapThemeChanged slot) then this xml could be out of date...
517  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
518  // just return the style name, we can instead set the overrides in mapThemeChanged and not here
519  mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
520  }
521 
522  // create the renderer job
523  Q_ASSERT( !mJob );
524  mJobCanceled = false;
525  if ( mUseParallelRendering )
526  mJob = new QgsMapRendererParallelJob( mSettings );
527  else
528  mJob = new QgsMapRendererSequentialJob( mSettings );
529  connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
530  mJob->setCache( mCache );
531 
532  mJob->start();
533 
534  // from now on we can accept refresh requests again
535  // this must be reset only after the job has been started, because
536  // some providers (yes, it's you WCS and AMS!) during preparation
537  // do network requests and start an internal event loop, which may
538  // end up calling refresh() and would schedule another refresh,
539  // deleting the one we have just started.
540  mRefreshScheduled = false;
541 
542  mMapUpdateTimer.start();
543 
544  emit renderStarting();
545 }
546 
547 void QgsMapCanvas::mapThemeChanged( const QString &theme )
548 {
549  if ( theme == mTheme )
550  {
551  // set the canvas layers to match the new layers contained in the map theme
552  // NOTE: we do this when the theme layers change and not when we are refreshing the map
553  // as setLayers() sets up necessary connections to handle changes to the layers
554  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
555  // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
556  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
557  // current state of the style. If changes were made to the style then this xml
558  // snapshot goes out of sync...
559  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
560  // just return the style name, we can instead set the overrides here and not in refreshMap()
561 
562  clearCache();
563  refresh();
564  }
565 }
566 
567 
568 void QgsMapCanvas::rendererJobFinished()
569 {
570  QgsDebugMsg( QString( "CANVAS finish! %1" ).arg( !mJobCanceled ) );
571 
572  mMapUpdateTimer.stop();
573 
574  // TODO: would be better to show the errors in message bar
575  Q_FOREACH ( const QgsMapRendererJob::Error &error, mJob->errors() )
576  {
577  QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) );
578  }
579 
580  if ( !mJobCanceled )
581  {
582  // take labeling results before emitting renderComplete, so labeling map tools
583  // connected to signal work with correct results
584  if ( !mJob->usedCachedLabels() )
585  {
586  delete mLabelingResults;
587  mLabelingResults = mJob->takeLabelingResults();
588  }
589 
590  QImage img = mJob->renderedImage();
591 
592  // emit renderComplete to get our decorations drawn
593  QPainter p( &img );
594  emit renderComplete( &p );
595 
596  QgsSettings settings;
597  if ( settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
598  {
599  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
600  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
601  }
602 
603  if ( mDrawRenderingStats )
604  {
605  int w = img.width(), h = img.height();
606  QFont fnt = p.font();
607  fnt.setBold( true );
608  p.setFont( fnt );
609  int lh = p.fontMetrics().height() * 2;
610  QRect r( 0, h - lh, w, lh );
611  p.setPen( Qt::NoPen );
612  p.setBrush( QColor( 0, 0, 0, 110 ) );
613  p.drawRect( r );
614  p.setPen( Qt::white );
615  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? "PARALLEL" : "SEQUENTIAL" ).arg( mJob->renderingTime() );
616  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
617  }
618 
619  p.end();
620 
621  mMap->setContent( img, imageRect( img, mSettings ) );
622  if ( mUsePreviewJobs )
623  startPreviewJobs();
624  }
625 
626  // now we are in a slot called from mJob - do not delete it immediately
627  // so the class is still valid when the execution returns to the class
628  mJob->deleteLater();
629  mJob = nullptr;
630 
631  emit mapCanvasRefreshed();
632 }
633 
634 void QgsMapCanvas::previewJobFinished()
635 {
636  QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
637  Q_ASSERT( job );
638 
639  if ( mMap )
640  {
641  mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
642  mPreviewJobs.removeAll( job );
643  delete job;
644  }
645 }
646 
647 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
648 {
649  // This is a hack to pass QgsMapCanvasItem::setRect what it
650  // expects (encoding of position and size of the item)
651  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
652  QgsPointXY topLeft = m2p.toMapPoint( 0, 0 );
653  double res = m2p.mapUnitsPerPixel();
654  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
655  return rect;
656 }
657 
659 {
660  return mUsePreviewJobs;
661 }
662 
664 {
665  mUsePreviewJobs = enabled;
666 }
667 
668 void QgsMapCanvas::mapUpdateTimeout()
669 {
670  if ( mJob )
671  {
672  const QImage &img = mJob->renderedImage();
673  mMap->setContent( img, imageRect( img, mSettings ) );
674  }
675 }
676 
678 {
679  if ( mJob )
680  {
681  QgsDebugMsg( "CANVAS stop rendering!" );
682  mJobCanceled = true;
683  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
684  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
685  mJob->cancelWithoutBlocking();
686  mJob = nullptr;
687  }
688 }
689 
690 //the format defaults to "PNG" if not specified
691 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
692 {
693  QPainter painter;
694  QImage image;
695 
696  //
697  //check if the optional QPaintDevice was supplied
698  //
699  if ( theQPixmap )
700  {
701  image = theQPixmap->toImage();
702  painter.begin( &image );
703 
704  // render
705  QgsMapRendererCustomPainterJob job( mSettings, &painter );
706  job.start();
707  job.waitForFinished();
708  emit renderComplete( &painter );
709  }
710  else //use the map view
711  {
712  image = mMap->contentImage().copy();
713  painter.begin( &image );
714  }
715 
716  // draw annotations
717  QStyleOptionGraphicsItem option;
718  option.initFrom( this );
719  QGraphicsItem *item = nullptr;
720  QListIterator<QGraphicsItem *> i( items() );
721  i.toBack();
722  while ( i.hasPrevious() )
723  {
724  item = i.previous();
725 
726  if ( !item || dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) )
727  {
728  continue;
729  }
730 
731  painter.save();
732 
733  QPointF itemScenePos = item->scenePos();
734  painter.translate( itemScenePos.x(), itemScenePos.y() );
735 
736  item->paint( &painter, &option );
737 
738  painter.restore();
739  }
740 
741  painter.end();
742  image.save( fileName, format.toLocal8Bit().data() );
743 
744  QFileInfo myInfo = QFileInfo( fileName );
745 
746  // build the world file name
747  QString outputSuffix = myInfo.suffix();
748  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
749  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
750  QFile myWorldFile( myWorldFileName );
751  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
752  {
753  return;
754  }
755  QTextStream myStream( &myWorldFile );
757 } // saveAsImage
758 
759 
760 
762 {
763  return mapSettings().visibleExtent();
764 } // extent
765 
767 {
768  return mapSettings().fullExtent();
769 } // extent
770 
771 
772 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
773 {
774  QgsRectangle current = extent();
775 
776  if ( ( r == current ) && magnified )
777  return;
778 
779  if ( r.isEmpty() )
780  {
781  if ( !mSettings.hasValidSettings() )
782  {
783  // we can't even just move the map center
784  QgsDebugMsg( "Empty extent - ignoring" );
785  return;
786  }
787 
788  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
789  QgsDebugMsg( "Empty extent - keeping old scale with new center!" );
790  setCenter( r.center() );
791  }
792  else
793  {
794  mSettings.setExtent( r, magnified );
795  }
796  emit extentsChanged();
797  updateScale();
798  if ( mLastExtent.size() > 20 )
799  mLastExtent.removeAt( 0 );
800 
801  //clear all extent items after current index
802  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
803  {
804  mLastExtent.removeAt( i );
805  }
806 
807  mLastExtent.append( extent() );
808 
809  // adjust history to no more than 20
810  if ( mLastExtent.size() > 20 )
811  {
812  mLastExtent.removeAt( 0 );
813  }
814 
815  // the last item is the current extent
816  mLastExtentIndex = mLastExtent.size() - 1;
817 
818  // update controls' enabled state
819  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
820  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
821 } // setExtent
822 
824 {
826  double x = center.x();
827  double y = center.y();
828  setExtent(
829  QgsRectangle(
830  x - r.width() / 2.0, y - r.height() / 2.0,
831  x + r.width() / 2.0, y + r.height() / 2.0
832  ),
833  true
834  );
835 } // setCenter
836 
838 {
840  return r.center();
841 }
842 
843 
845 {
846  return mapSettings().rotation();
847 } // rotation
848 
849 void QgsMapCanvas::setRotation( double degrees )
850 {
851  double current = rotation();
852 
853  if ( degrees == current )
854  return;
855 
856  mSettings.setRotation( degrees );
857  emit rotationChanged( degrees );
858  emit extentsChanged(); // visible extent changes with rotation
859 } // setRotation
860 
861 
863 {
864  emit scaleChanged( mapSettings().scale() );
865 }
866 
867 
869 {
871  // If the full extent is an empty set, don't do the zoom
872  if ( !extent.isEmpty() )
873  {
874  // Add a 5% margin around the full extent
875  extent.scale( 1.05 );
876  setExtent( extent );
877  }
878  refresh();
879 
880 } // zoomToFullExtent
881 
882 
883 
885 {
886  if ( mLastExtentIndex > 0 )
887  {
888  mLastExtentIndex--;
889  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
890  emit extentsChanged();
891  updateScale();
892  refresh();
893  // update controls' enabled state
894  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
895  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
896  }
897 
898 } // zoomToPreviousExtent
899 
901 {
902  if ( mLastExtentIndex < mLastExtent.size() - 1 )
903  {
904  mLastExtentIndex++;
905  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
906  emit extentsChanged();
907  updateScale();
908  refresh();
909  // update controls' enabled state
910  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
911  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
912  }
913 }// zoomToNextExtent
914 
916 {
917  mLastExtent.clear(); // clear the zoom history list
918  mLastExtent.append( extent() ) ; // set the current extent in the list
919  mLastExtentIndex = mLastExtent.size() - 1;
920  // update controls' enabled state
921  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
922  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
923 }// clearExtentHistory
924 
926 {
927  if ( !layer )
928  {
929  // use current layer by default
930  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
931  }
932 
933  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
934  return;
935 
936  QgsRectangle rect = layer->boundingBoxOfSelected();
937  if ( rect.isNull() )
938  {
939  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), QgsMessageBar::WARNING );
940  return;
941  }
942 
943  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
944  zoomToFeatureExtent( rect );
945 } // zoomToSelected
946 
948 {
949  // no selected features, only one selected point feature
950  //or two point features with the same x- or y-coordinates
951  if ( rect.isEmpty() )
952  {
953  // zoom in
954  QgsPointXY c = rect.center();
955  rect = extent();
956  rect.scale( 1.0, &c );
957  }
958  //zoom to an area
959  else
960  {
961  // Expand rect to give a bit of space around the selected
962  // objects so as to keep them clear of the map boundaries
963  // The same 5% should apply to all margins.
964  rect.scale( 1.05 );
965  }
966 
967  setExtent( rect );
968  refresh();
969 }
970 
972 {
973  if ( !layer )
974  {
975  return;
976  }
977 
978  QgsRectangle bbox;
979  QString errorMsg;
980  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
981  {
982  zoomToFeatureExtent( bbox );
983  }
984  else
985  {
986  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, QgsMessageBar::WARNING );
987  }
988 
989 }
990 
992 {
993  if ( !layer )
994  {
995  return;
996  }
997 
998  QgsRectangle bbox;
999  QString errorMsg;
1000  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1001  {
1002  setCenter( bbox.center() );
1003  refresh();
1004  }
1005  else
1006  {
1007  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, QgsMessageBar::WARNING );
1008  }
1009 }
1010 
1011 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1012 {
1013  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setSubsetOfAttributes( QgsAttributeList() ) );
1014  bbox.setMinimal();
1015  QgsFeature fet;
1016  int featureCount = 0;
1017  errorMsg.clear();
1018 
1019  while ( it.nextFeature( fet ) )
1020  {
1021  QgsGeometry geom = fet.geometry();
1022  if ( geom.isNull() )
1023  {
1024  errorMsg = tr( "Feature does not have a geometry" );
1025  }
1026  else if ( geom.geometry()->isEmpty() )
1027  {
1028  errorMsg = tr( "Feature geometry is empty" );
1029  }
1030  if ( !errorMsg.isEmpty() )
1031  {
1032  return false;
1033  }
1035  bbox.combineExtentWith( r );
1036  featureCount++;
1037  }
1038 
1039  if ( featureCount != ids.count() )
1040  {
1041  errorMsg = tr( "Feature not found" );
1042  return false;
1043  }
1044 
1045  return true;
1046 }
1047 
1049 {
1050  if ( !layer )
1051  {
1052  // use current layer by default
1053  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1054  }
1055 
1056  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1057  return;
1058 
1059  QgsRectangle rect = layer->boundingBoxOfSelected();
1060  if ( rect.isNull() )
1061  {
1062  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), QgsMessageBar::WARNING );
1063  return;
1064  }
1065 
1066  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1067  setCenter( rect.center() );
1068  refresh();
1069 }
1070 
1071 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1072 {
1073  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1074  {
1075  emit keyPressed( e );
1076  return;
1077  }
1078 
1079  if ( ! mCanvasProperties->mouseButtonDown )
1080  {
1081  // Don't want to interfer with mouse events
1082 
1083  QgsRectangle currentExtent = mapSettings().visibleExtent();
1084  double dx = std::fabs( currentExtent.width() / 4 );
1085  double dy = std::fabs( currentExtent.height() / 4 );
1086 
1087  switch ( e->key() )
1088  {
1089  case Qt::Key_Left:
1090  QgsDebugMsg( "Pan left" );
1091  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1092  refresh();
1093  break;
1094 
1095  case Qt::Key_Right:
1096  QgsDebugMsg( "Pan right" );
1097  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1098  refresh();
1099  break;
1100 
1101  case Qt::Key_Up:
1102  QgsDebugMsg( "Pan up" );
1103  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1104  refresh();
1105  break;
1106 
1107  case Qt::Key_Down:
1108  QgsDebugMsg( "Pan down" );
1109  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1110  refresh();
1111  break;
1112 
1113 
1114 
1115  case Qt::Key_Space:
1116  QgsDebugMsg( "Pressing pan selector" );
1117 
1118  //mCanvasProperties->dragging = true;
1119  if ( ! e->isAutoRepeat() )
1120  {
1121  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1122  mCanvasProperties->panSelectorDown = true;
1123  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1124  }
1125  break;
1126 
1127  case Qt::Key_PageUp:
1128  QgsDebugMsg( "Zoom in" );
1129  zoomIn();
1130  break;
1131 
1132  case Qt::Key_PageDown:
1133  QgsDebugMsg( "Zoom out" );
1134  zoomOut();
1135  break;
1136 
1137 #if 0
1138  case Qt::Key_P:
1139  mUseParallelRendering = !mUseParallelRendering;
1140  refresh();
1141  break;
1142 
1143  case Qt::Key_S:
1144  mDrawRenderingStats = !mDrawRenderingStats;
1145  refresh();
1146  break;
1147 #endif
1148 
1149  default:
1150  // Pass it on
1151  if ( mMapTool )
1152  {
1153  mMapTool->keyPressEvent( e );
1154  }
1155  else e->ignore();
1156 
1157  QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
1158  }
1159  }
1160 
1161  emit keyPressed( e );
1162 
1163 } //keyPressEvent()
1164 
1165 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1166 {
1167  QgsDebugMsg( "keyRelease event" );
1168 
1169  switch ( e->key() )
1170  {
1171  case Qt::Key_Space:
1172  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1173  {
1174  QgsDebugMsg( "Releasing pan selector" );
1175  QApplication::restoreOverrideCursor();
1176  mCanvasProperties->panSelectorDown = false;
1177  panActionEnd( mCanvasProperties->mouseLastXY );
1178  }
1179  break;
1180 
1181  default:
1182  // Pass it on
1183  if ( mMapTool )
1184  {
1185  mMapTool->keyReleaseEvent( e );
1186  }
1187  else e->ignore();
1188 
1189  QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
1190  }
1191 
1192  emit keyReleased( e );
1193 
1194 } //keyReleaseEvent()
1195 
1196 
1198 {
1199  // call handler of current map tool
1200  if ( mMapTool )
1201  {
1202  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1203  mMapTool->canvasDoubleClickEvent( me.get() );
1204  }
1205 }// mouseDoubleClickEvent
1206 
1207 
1208 void QgsMapCanvas::beginZoomRect( QPoint pos )
1209 {
1210  mZoomRect.setRect( 0, 0, 0, 0 );
1211  QApplication::setOverrideCursor( mZoomCursor );
1212  mZoomDragging = true;
1213  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1214  QColor color( Qt::blue );
1215  color.setAlpha( 63 );
1216  mZoomRubberBand->setColor( color );
1217  mZoomRect.setTopLeft( pos );
1218 }
1219 
1220 void QgsMapCanvas::endZoomRect( QPoint pos )
1221 {
1222  mZoomDragging = false;
1223  mZoomRubberBand.reset( nullptr );
1224  QApplication::restoreOverrideCursor();
1225 
1226  // store the rectangle
1227  mZoomRect.setRight( pos.x() );
1228  mZoomRect.setBottom( pos.y() );
1229 
1230  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1231  {
1232  //probably a mistake - would result in huge zoom!
1233  return;
1234  }
1235 
1236  //account for bottom right -> top left dragging
1237  mZoomRect = mZoomRect.normalized();
1238 
1239  // set center and zoom
1240  const QSize &zoomRectSize = mZoomRect.size();
1241  const QSize &canvasSize = mSettings.outputSize();
1242  double sfx = ( double )zoomRectSize.width() / canvasSize.width();
1243  double sfy = ( double )zoomRectSize.height() / canvasSize.height();
1244  double sf = std::max( sfx, sfy );
1245 
1246  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1247 
1248  zoomByFactor( sf, &c );
1249  refresh();
1250 }
1251 
1252 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1253 {
1254  //use middle mouse button for panning, map tools won't receive any events in that case
1255  if ( e->button() == Qt::MidButton )
1256  {
1257  mCanvasProperties->panSelectorDown = true;
1258  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1259  }
1260  else
1261  {
1262  // call handler of current map tool
1263  if ( mMapTool )
1264  {
1265  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1266  && e->modifiers() & Qt::ShiftModifier )
1267  {
1268  beginZoomRect( e->pos() );
1269  return;
1270  }
1271  else
1272  {
1273  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1274  mMapTool->canvasPressEvent( me.get() );
1275  }
1276  }
1277  }
1278 
1279  if ( mCanvasProperties->panSelectorDown )
1280  {
1281  return;
1282  }
1283 
1284  mCanvasProperties->mouseButtonDown = true;
1285  mCanvasProperties->rubberStartPoint = e->pos();
1286 
1287 } // mousePressEvent
1288 
1289 
1290 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1291 {
1292  //use middle mouse button for panning, map tools won't receive any events in that case
1293  if ( e->button() == Qt::MidButton )
1294  {
1295  mCanvasProperties->panSelectorDown = false;
1296  panActionEnd( mCanvasProperties->mouseLastXY );
1297  }
1298  else if ( e->button() == Qt::BackButton )
1299  {
1301  return;
1302  }
1303  else if ( e->button() == Qt::ForwardButton )
1304  {
1305  zoomToNextExtent();
1306  return;
1307  }
1308  else
1309  {
1310  if ( mZoomDragging && e->button() == Qt::LeftButton )
1311  {
1312  endZoomRect( e->pos() );
1313  return;
1314  }
1315 
1316  // call handler of current map tool
1317  if ( mMapTool )
1318  {
1319  // right button was pressed in zoom tool? return to previous non zoom tool
1320  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1321  {
1322  QgsDebugMsg( "Right click in map tool zoom or pan, last tool is " +
1323  QString( mLastNonZoomMapTool ? "not null." : "null." ) );
1324 
1325  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1326 
1327  // change to older non-zoom tool
1328  if ( mLastNonZoomMapTool
1329  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1330  || ( vlayer && vlayer->isEditable() ) ) )
1331  {
1332  QgsMapTool *t = mLastNonZoomMapTool;
1333  mLastNonZoomMapTool = nullptr;
1334  setMapTool( t );
1335  }
1336  return;
1337  }
1338  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1339  mMapTool->canvasReleaseEvent( me.get() );
1340  }
1341  }
1342 
1343 
1344  mCanvasProperties->mouseButtonDown = false;
1345 
1346  if ( mCanvasProperties->panSelectorDown )
1347  return;
1348 
1349 } // mouseReleaseEvent
1350 
1351 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1352 {
1353  QGraphicsView::resizeEvent( e );
1354  mResizeTimer->start( 500 );
1355 
1356  QSize lastSize = viewport()->size();
1357 
1358  mSettings.setOutputSize( lastSize );
1359 
1360  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1361 
1362  moveCanvasContents( true );
1363 
1364  updateScale();
1365 
1366  //refresh();
1367 
1368  emit extentsChanged();
1369 }
1370 
1371 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1372 {
1373  // no custom event handling anymore
1374 
1375  QGraphicsView::paintEvent( e );
1376 } // paintEvent
1377 
1379 {
1380  QList<QGraphicsItem *> list = mScene->items();
1381  QList<QGraphicsItem *>::iterator it = list.begin();
1382  while ( it != list.end() )
1383  {
1384  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( *it );
1385 
1386  if ( item )
1387  {
1388  item->updatePosition();
1389  }
1390 
1391  ++it;
1392  }
1393 }
1394 
1395 
1396 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1397 {
1398  // Zoom the map canvas in response to a mouse wheel event. Moving the
1399  // wheel forward (away) from the user zooms in
1400 
1401  QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
1402 
1403  if ( mMapTool )
1404  {
1405  mMapTool->wheelEvent( e );
1406  if ( e->isAccepted() )
1407  return;
1408  }
1409 
1410  double zoomFactor = mWheelZoomFactor;
1411 
1412  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1413  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
1414 
1415  if ( e->modifiers() & Qt::ControlModifier )
1416  {
1417  //holding ctrl while wheel zooming results in a finer zoom
1418  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1419  }
1420 
1421  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1422 
1423  // zoom map to mouse cursor by scaling
1424  QgsPointXY oldCenter = center();
1425  QgsPointXY mousePos( getCoordinateTransform()->toMapPoint( e->x(), e->y() ) );
1426  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1427  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1428 
1429  zoomByFactor( signedWheelFactor, &newCenter );
1430 }
1431 
1432 void QgsMapCanvas::setWheelFactor( double factor )
1433 {
1434  mWheelZoomFactor = factor;
1435 }
1436 
1438 {
1439  // magnification is alreday handled in zoomByFactor
1440  zoomByFactor( 1 / mWheelZoomFactor );
1441 }
1442 
1444 {
1445  // magnification is alreday handled in zoomByFactor
1446  zoomByFactor( mWheelZoomFactor );
1447 }
1448 
1449 void QgsMapCanvas::zoomScale( double newScale )
1450 {
1451  zoomByFactor( newScale / scale() );
1452 }
1453 
1454 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1455 {
1456  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1457 
1458  if ( mScaleLocked )
1459  {
1461  }
1462  else
1463  {
1464  // transform the mouse pos to map coordinates
1467  r.scale( scaleFactor, &center );
1468  setExtent( r, true );
1469  refresh();
1470  }
1471 }
1472 
1473 void QgsMapCanvas::setScaleLocked( bool isLocked )
1474 {
1475  mScaleLocked = isLocked;
1476 }
1477 
1478 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1479 {
1480  mCanvasProperties->mouseLastXY = e->pos();
1481 
1482  if ( mCanvasProperties->panSelectorDown )
1483  {
1484  panAction( e );
1485  }
1486  else if ( mZoomDragging )
1487  {
1488  mZoomRect.setBottomRight( e->pos() );
1489  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1490  mZoomRubberBand->show();
1491  }
1492  else
1493  {
1494  // call handler of current map tool
1495  if ( mMapTool )
1496  {
1497  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1498  mMapTool->canvasMoveEvent( me.get() );
1499  }
1500  }
1501 
1502  // show x y on status bar
1503  QPoint xy = e->pos();
1505  emit xyCoordinates( coord );
1506 }
1507 
1509 {
1510  if ( !tool )
1511  return;
1512 
1513  if ( mMapTool )
1514  {
1515  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1516  mMapTool->deactivate();
1517  }
1518 
1519  if ( ( tool->flags() & QgsMapTool::Transient )
1520  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1521  {
1522  // if zoom or pan tool will be active, save old tool
1523  // to bring it back on right click
1524  // (but only if it wasn't also zoom or pan tool)
1525  mLastNonZoomMapTool = mMapTool;
1526  }
1527  else
1528  {
1529  mLastNonZoomMapTool = nullptr;
1530  }
1531 
1532  QgsMapTool *oldTool = mMapTool;
1533 
1534  // set new map tool and activate it
1535  mMapTool = tool;
1536  if ( mMapTool )
1537  {
1538  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1539  mMapTool->activate();
1540  }
1541 
1542  emit mapToolSet( mMapTool, oldTool );
1543 } // setMapTool
1544 
1546 {
1547  if ( mMapTool && mMapTool == tool )
1548  {
1549  mMapTool->deactivate();
1550  mMapTool = nullptr;
1551  emit mapToolSet( nullptr, mMapTool );
1552  setCursor( Qt::ArrowCursor );
1553  }
1554 
1555  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1556  {
1557  mLastNonZoomMapTool = nullptr;
1558  }
1559 }
1560 
1561 void QgsMapCanvas::setCanvasColor( const QColor &color )
1562 {
1563  // background of map's pixmap
1564  mSettings.setBackgroundColor( color );
1565 
1566  // background of the QGraphicsView
1567  QBrush bgBrush( color );
1568  setBackgroundBrush( bgBrush );
1569 #if 0
1570  QPalette palette;
1571  palette.setColor( backgroundRole(), color );
1572  setPalette( palette );
1573 #endif
1574 
1575  // background of QGraphicsScene
1576  mScene->setBackgroundBrush( bgBrush );
1577 }
1578 
1580 {
1581  return mScene->backgroundBrush().color();
1582 }
1583 
1584 void QgsMapCanvas::setSelectionColor( const QColor &color )
1585 {
1586  mSettings.setSelectionColor( color );
1587 }
1588 
1590 {
1591  return mapSettings().layers().size();
1592 } // layerCount
1593 
1594 
1595 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1596 {
1597  return mapSettings().layers();
1598 }
1599 
1600 
1602 {
1603  // called when a layer has changed visibility setting
1604 
1605  refresh();
1606 
1607 } // layerStateChange
1608 
1610 {
1611  // called when a layer's CRS has been changed
1612  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
1613  QString destAuthId = mSettings.destinationCrs().authid();
1614  getDatumTransformInfo( layer, layer->crs().authid(), destAuthId );
1615 
1616 } // layerCrsChange
1617 
1618 
1619 void QgsMapCanvas::freeze( bool frozen )
1620 {
1621  mFrozen = frozen;
1622 }
1623 
1625 {
1626  return mFrozen;
1627 }
1628 
1629 
1631 {
1632  return mapSettings().mapUnitsPerPixel();
1633 } // mapUnitsPerPixel
1634 
1636 {
1637  return mapSettings().mapUnits();
1638 }
1639 
1640 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1641 {
1642  return mSettings.layerStyleOverrides();
1643 }
1644 
1645 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1646 {
1647  if ( overrides == mSettings.layerStyleOverrides() )
1648  return;
1649 
1650  mSettings.setLayerStyleOverrides( overrides );
1651  clearCache();
1653 }
1654 
1655 void QgsMapCanvas::setTheme( const QString &theme )
1656 {
1657  if ( mTheme == theme )
1658  return;
1659 
1660  clearCache();
1661  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1662  {
1663  mTheme.clear();
1664  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1665  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1666  emit themeChanged( QString() );
1667  }
1668  else
1669  {
1670  mTheme = theme;
1671  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1672  emit themeChanged( theme );
1673  }
1674 }
1675 
1677 {
1678  mRenderFlag = flag;
1679 
1680  if ( mRenderFlag )
1681  {
1682  refresh();
1683  }
1684  else
1685  stopRendering();
1686 }
1687 
1688 #if 0
1689 void QgsMapCanvas::connectNotify( const char *signal )
1690 {
1691  Q_UNUSED( signal );
1692  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1693 } //connectNotify
1694 #endif
1695 
1697 {
1698  QString destAuthId = mSettings.destinationCrs().authid();
1699  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1700  {
1701  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
1702  if ( vl && vl->geometryType() == QgsWkbTypes::NullGeometry )
1703  continue;
1704 
1705  // if there are more options, ask the user which datum transform to use
1706  if ( !mSettings.datumTransformStore().hasEntryForLayer( layer ) )
1707  getDatumTransformInfo( layer, layer->crs().authid(), destAuthId );
1708  }
1709 }
1710 
1711 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1712 {
1713  if ( !deferred )
1714  refresh();
1715 }
1716 
1717 void QgsMapCanvas::autoRefreshTriggered()
1718 {
1719  if ( mJob )
1720  {
1721  // canvas is currently being redrawn, so we skip this auto refresh
1722  // otherwise we could get stuck in the situation where an auto refresh is triggered
1723  // too often to allow the canvas to ever finish rendering
1724  return;
1725  }
1726 
1727  refresh();
1728 }
1729 
1730 void QgsMapCanvas::updateAutoRefreshTimer()
1731 {
1732  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1733  // trigger a map refresh on this minimum interval
1734  int minAutoRefreshInterval = -1;
1735  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1736  {
1737  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1738  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1739  }
1740 
1741  if ( minAutoRefreshInterval > 0 )
1742  {
1743  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1744  mAutoRefreshTimer.start();
1745  }
1746  else
1747  {
1748  mAutoRefreshTimer.stop();
1749  }
1750 }
1751 
1752 void QgsMapCanvas::projectThemesChanged()
1753 {
1754  if ( mTheme.isEmpty() )
1755  return;
1756 
1757  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1758  {
1759  // theme has been removed - stop following
1760  setTheme( QString() );
1761  }
1762 
1763 }
1764 
1766 {
1767  return mMapTool;
1768 }
1769 
1770 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
1771 {
1772  // move map image and other items to standard position
1773  moveCanvasContents( true ); // true means reset
1774 
1775  // use start and end box points to calculate the extent
1776  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
1778 
1779  // modify the center
1780  double dx = end.x() - start.x();
1781  double dy = end.y() - start.y();
1782  QgsPointXY c = center();
1783  c.set( c.x() - dx, c.y() - dy );
1784  setCenter( c );
1785 
1786  refresh();
1787 }
1788 
1789 void QgsMapCanvas::panAction( QMouseEvent *e )
1790 {
1791  Q_UNUSED( e );
1792 
1793  // move all map canvas items
1795 }
1796 
1798 {
1799  QPoint pnt( 0, 0 );
1800  if ( !reset )
1801  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
1802 
1803  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
1804 }
1805 
1807 {
1808  return mCanvasProperties->mouseLastXY;
1809 }
1810 
1811 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
1812 {
1813  if ( !mPreviewEffect )
1814  {
1815  return;
1816  }
1817 
1818  mPreviewEffect->setEnabled( previewEnabled );
1819 }
1820 
1822 {
1823  if ( !mPreviewEffect )
1824  {
1825  return false;
1826  }
1827 
1828  return mPreviewEffect->isEnabled();
1829 }
1830 
1832 {
1833  if ( !mPreviewEffect )
1834  {
1835  return;
1836  }
1837 
1838  mPreviewEffect->setMode( mode );
1839 }
1840 
1842 {
1843  if ( !mPreviewEffect )
1844  {
1846  }
1847 
1848  return mPreviewEffect->mode();
1849 }
1850 
1852 {
1853  if ( !mSnappingUtils )
1854  {
1855  // associate a dummy instance, but better than null pointer
1856  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
1857  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
1858  }
1859  return mSnappingUtils;
1860 }
1861 
1863 {
1864  mSnappingUtils = utils;
1865 }
1866 
1867 void QgsMapCanvas::readProject( const QDomDocument &doc )
1868 {
1869  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
1870  if ( nodes.count() )
1871  {
1872  QDomNode node = nodes.item( 0 );
1873 
1874  // Search the specific MapCanvas node using the name
1875  if ( nodes.count() > 1 )
1876  {
1877  for ( int i = 0; i < nodes.size(); ++i )
1878  {
1879  QDomElement elementNode = nodes.at( i ).toElement();
1880 
1881  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
1882  {
1883  node = nodes.at( i );
1884  break;
1885  }
1886  }
1887  }
1888 
1889  QgsMapSettings tmpSettings;
1890  tmpSettings.readXml( node );
1891  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
1892  {
1893  // never manually set the crs for the main canvas - this is instead connected to the project CRS
1894  setDestinationCrs( tmpSettings.destinationCrs() );
1895  }
1896  setExtent( tmpSettings.extent() );
1897  setRotation( tmpSettings.rotation() );
1898  mSettings.datumTransformStore() = tmpSettings.datumTransformStore();
1900 
1901  clearExtentHistory(); // clear the extent history on project load
1902 
1903  QDomElement elem = node.toElement();
1904  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
1905  {
1906  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
1907  {
1908  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
1909  }
1910  }
1911  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
1912  }
1913  else
1914  {
1915  QgsDebugMsg( "Couldn't read mapcanvas information from project" );
1916  }
1917 }
1918 
1919 void QgsMapCanvas::writeProject( QDomDocument &doc )
1920 {
1921  // create node "mapcanvas" and call mMapRenderer->writeXml()
1922 
1923  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1924  if ( !nl.count() )
1925  {
1926  QgsDebugMsg( "Unable to find qgis element in project file" );
1927  return;
1928  }
1929  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1930 
1931  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
1932  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
1933  if ( !mTheme.isEmpty() )
1934  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
1935  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
1936  qgisNode.appendChild( mapcanvasNode );
1937 
1938  mSettings.writeXml( mapcanvasNode, doc );
1939  // TODO: store only units, extent, projections, dest CRS
1940 }
1941 
1942 void QgsMapCanvas::getDatumTransformInfo( const QgsMapLayer *ml, const QString &srcAuthId, const QString &destAuthId )
1943 {
1944  if ( !ml )
1945  {
1946  return;
1947  }
1948 
1949  //check if default datum transformation available
1950  QgsSettings s;
1951  QString settingsString = "/Projections/" + srcAuthId + "//" + destAuthId;
1952  QVariant defaultSrcTransform = s.value( settingsString + "_srcTransform" );
1953  QVariant defaultDestTransform = s.value( settingsString + "_destTransform" );
1954  if ( defaultSrcTransform.isValid() && defaultDestTransform.isValid() )
1955  {
1956  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, defaultSrcTransform.toInt(), defaultDestTransform.toInt() );
1957  return;
1958  }
1959 
1962 
1963  if ( !s.value( QStringLiteral( "/Projections/showDatumTransformDialog" ), false ).toBool() )
1964  {
1965  // just use the default transform
1966  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, -1, -1 );
1967  return;
1968  }
1969 
1970  //get list of datum transforms
1971  QList< QList< int > > dt = QgsCoordinateTransform::datumTransformations( srcCRS, destCRS );
1972  if ( dt.size() < 2 )
1973  {
1974  return;
1975  }
1976 
1977  //if several possibilities: present dialog
1978  QgsDatumTransformDialog d( ml->name(), dt );
1979  d.setDatumTransformInfo( srcCRS.authid(), destCRS.authid() );
1980  if ( d.exec() == QDialog::Accepted )
1981  {
1982  int srcTransform = -1;
1983  int destTransform = -1;
1984  QList<int> t = d.selectedDatumTransform();
1985  if ( !t.isEmpty() )
1986  {
1987  srcTransform = t.at( 0 );
1988  }
1989  if ( t.size() > 1 )
1990  {
1991  destTransform = t.at( 1 );
1992  }
1993  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, srcTransform, destTransform );
1994  if ( d.rememberSelection() )
1995  {
1996  s.setValue( settingsString + "_srcTransform", srcTransform );
1997  s.setValue( settingsString + "_destTransform", destTransform );
1998  }
1999  }
2000  else
2001  {
2002  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, -1, -1 );
2003  }
2004 }
2005 
2006 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center )
2007 {
2008  if ( mScaleLocked )
2009  {
2010  // zoom map to mouse cursor by magnifying
2012  }
2013  else
2014  {
2016  r.scale( scaleFactor, center );
2017  setExtent( r, true );
2018  refresh();
2019  }
2020 }
2021 
2023 {
2024  // Find out which layer it was that sent the signal.
2025  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
2026  emit selectionChanged( layer );
2027  refresh();
2028 }
2029 
2030 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *e )
2031 {
2032  // By default graphics view delegates the drag events to graphics items.
2033  // But we do not want that and by ignoring the drag enter we let the
2034  // parent (e.g. QgisApp) to handle drops of map layers etc.
2035  e->ignore();
2036 }
2037 
2038 void QgsMapCanvas::mapToolDestroyed()
2039 {
2040  QgsDebugMsg( "maptool destroyed" );
2041  mMapTool = nullptr;
2042 }
2043 
2044 bool QgsMapCanvas::event( QEvent *e )
2045 {
2046  if ( !QTouchDevice::devices().empty() )
2047  {
2048  if ( e->type() == QEvent::Gesture )
2049  {
2050  // call handler of current map tool
2051  if ( mMapTool )
2052  {
2053  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2054  }
2055  }
2056  }
2057 
2058  // pass other events to base class
2059  return QGraphicsView::event( e );
2060 }
2061 
2063 {
2064  // reload all layers in canvas
2065  for ( int i = 0; i < layerCount(); i++ )
2066  {
2067  QgsMapLayer *l = layer( i );
2068  if ( l )
2069  l->reload();
2070  }
2071 
2072  // clear the cache
2073  clearCache();
2074 
2075  // and then refresh
2076  refresh();
2077 }
2078 
2080 {
2081  while ( mRefreshScheduled || mJob )
2082  {
2083  QgsApplication::processEvents();
2084  }
2085 }
2086 
2088 {
2089  mSettings.setSegmentationTolerance( tolerance );
2090 }
2091 
2093 {
2094  mSettings.setSegmentationToleranceType( type );
2095 }
2096 
2097 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2098 {
2099  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2100  QList<QGraphicsItem *> itemList = mScene->items();
2101  QList<QGraphicsItem *>::iterator it = itemList.begin();
2102  for ( ; it != itemList.end(); ++it )
2103  {
2104  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( *it );
2105  if ( aItem )
2106  {
2107  annotationItemList.push_back( aItem );
2108  }
2109  }
2110 
2111  return annotationItemList;
2112 }
2113 
2115 {
2116  mAnnotationsVisible = show;
2117  Q_FOREACH ( QgsMapCanvasAnnotationItem *item, annotationItems() )
2118  {
2119  item->setVisible( show );
2120  }
2121 }
2122 
2124 {
2125  mSettings.setLabelingEngineSettings( settings );
2126 }
2127 
2129 {
2130  return mSettings.labelingEngineSettings();
2131 }
2132 
2133 void QgsMapCanvas::startPreviewJobs()
2134 {
2135  stopPreviewJobs(); //just in case still running
2136  schedulePreviewJob( 0 );
2137 }
2138 
2139 void QgsMapCanvas::startPreviewJob( int number )
2140 {
2141  QgsRectangle mapRect = mSettings.visibleExtent();
2142 
2143  if ( number == 4 )
2144  number += 1;
2145 
2146  int j = number / 3;
2147  int i = number % 3;
2148 
2149  //copy settings, only update extent
2150  QgsMapSettings jobSettings = mSettings;
2151 
2152  double dx = ( i - 1 ) * mapRect.width();
2153  double dy = ( 1 - j ) * mapRect.height();
2154  QgsRectangle jobExtent = mapRect;
2155 
2156  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
2157  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
2158  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
2159  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
2160 
2161  jobSettings.setExtent( jobExtent );
2162  jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
2163 
2164  QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
2165  mPreviewJobs.append( job );
2166  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2167  job->start();
2168 
2169  if ( number < 8 )
2170  {
2171  schedulePreviewJob( number + 1 );
2172  }
2173 }
2174 
2175 void QgsMapCanvas::stopPreviewJobs()
2176 {
2177  mPreviewTimer.stop();
2178  QList< QgsMapRendererQImageJob * >::const_iterator it = mPreviewJobs.constBegin();
2179  for ( ; it != mPreviewJobs.constEnd(); ++it )
2180  {
2181  if ( *it )
2182  {
2183  disconnect( *it, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2184  connect( *it, &QgsMapRendererQImageJob::finished, *it, &QgsMapRendererQImageJob::deleteLater );
2185  ( *it )->cancelWithoutBlocking();
2186  }
2187  }
2188  mPreviewJobs.clear();
2189 }
2190 
2191 void QgsMapCanvas::schedulePreviewJob( int number )
2192 {
2193  mPreviewTimer.setSingleShot( true );
2194  mPreviewTimer.setInterval( 250 );
2195  disconnect( mPreviewTimerConnection );
2196  mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, [ = ]()
2197  {
2198  startPreviewJob( number );
2199  }
2200  );
2201  mPreviewTimer.start();
2202 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
bool previewJobsEnabled() const
Returns true if canvas map preview jobs (low priority render jobs which render portions of the view j...
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:116
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:744
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:143
void setRenderFlag(bool flag)
Sets whether a user has disabled canvas renders via the GUI.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds) ...
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
void zoomToNextExtent()
Zoom to the next extent (view)
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void zoomWithCenter(int x, int y, bool zoomIn)
Zooms in/out with a given center.
void setCanvasColor(const QColor &_newVal)
Write property of QColor bgColor.
void setCenter(const QgsPointXY &center)
Set the center of the map canvas, in geographical coordinates.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
static QList< QList< int > > datumTransformations(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destinationCrs)
Returns list of datum transformations for the given src and dest CRS.
QList< QgsMapLayer * > layers() const
Return the list of layers shown within the map canvas.
const char * zoom_in[]
Bitmap cursors for map operations.
Definition: qgscursors.cpp:23
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
double magnificationFactor() const
Return the magnification factor.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:75
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
void setPreviewJobsEnabled(bool enabled)
Sets whether canvas map preview jobs (low priority render jobs which render portions of the view just...
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:343
Allow zooming by rectangle (by holding shift and dragging) while the tool is active.
Definition: qgsmaptool.h:92
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
void enableAntiAliasing(bool flag)
used to determine if anti-aliasing is enabled or not
An abstract class for items that can be placed on the map canvas.
void setCurrentLayer(QgsMapLayer *layer)
int layerCount() const
return number of layers on the map
const QgsDatumTransformStore & datumTransformStore() const
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
void moveCanvasContents(bool reset=false)
called when panning is in action, reset indicates end of panning
void crsChanged()
Emit a signal that layer&#39;s CRS has been reset.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setFlags(QgsMapSettings::Flags flags)
Set combination of flags that will be used for rendering.
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
constexpr double CANVAS_MAGNIFICATION_MAX
Maximum magnification level allowed in map canvases.
Definition: qgsguiutils.h:68
void readProject(const QDomDocument &)
called to read map canvas settings from project
bool panSelectorDown
Flag to indicate the pan selector key is held down by user.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:92
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:210
Enable drawing of labels on top of the map.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
void zoomLastStatusChanged(bool)
Emitted when zoom last status changed.
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
virtual void canvasPressEvent(QgsMapMouseEvent *e)
Mouse press event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:153
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
void magnificationChanged(double)
Emitted when the scale of the map changes.
QString what() const
Definition: qgsexception.h:48
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void updateScale()
Emits signal scaleChanged to update scale in main window.
QgsUnitTypes::DistanceUnit mapUnits() const
Get units of map&#39;s geographical coordinates - used for scale calculation.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
void resizeEvent(QResizeEvent *e) override
Overridden resize event.
void setDatumTransformInfo(const QString &srcCRSauthId, const QString &destCRSauthId)
Deprecated to be deleted, stuff from here should be moved elsewhere.
void setMapTool(QgsMapTool *mapTool)
Sets the map tool currently being used on the canvas.
void enableMapTileRendering(bool flag)
sets map tile rendering flag
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:81
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:173
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:87
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:123
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:80
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.
void mouseDoubleClickEvent(QMouseEvent *e) override
Overridden mouse double-click event.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void panToSelected(QgsVectorLayer *layer=nullptr)
Pan to the selected features of current (vector) layer keeping same extent.
virtual bool isSpatial() const override
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
void setWheelFactor(double factor)
set wheel zoom factor (should be greater than 1)
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
void messageEmitted(const QString &title, const QString &message, QgsMessageBar::MessageLevel=QgsMessageBar::INFO)
emit a message (usually to be displayed in a message bar)
void destinationCrsChanged()
Emitted when map CRS has changed.
void updateDatumTransformEntries()
Make sure the datum transform store is properly populated.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
bool isFrozen() const
Returns true if canvas is frozen.
void setCachingEnabled(bool enabled)
Set whether to cache images of rendered layers.
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:97
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:163
void setMode(PreviewMode mode)
Sets the mode for the preview effect, which controls how the effect modifies a widgets appearance...
void renderStarting()
Emitted when the canvas is about to be rendered.
const QgsMapToPixel & mapToPixel() const
void keyPressed(QKeyEvent *e)
Emit key press event.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
void zoomOut()
Zoom out with fixed factor.
Enable drawing of vertex markers for layers in editing mode.
void waitWhileRendering()
Blocks until the rendering job has finished.
constexpr double CANVAS_MAGNIFICATION_MIN
Minimum magnification level allowed in map canvases.
Definition: qgsguiutils.h:60
void zoomToPreviousExtent()
Zoom to the previous extent (view)
bool isDrawing()
Find out whether rendering is in progress.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
double x
Definition: qgspointxy.h:46
void zoomToSelected(QgsVectorLayer *layer=nullptr)
Zoom to the extent of the selected features of current (vector) layer.
A class to represent a vector.
Definition: qgsvector.h:26
PreviewMode mode() const
Returns the mode used for the preview effect.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
virtual void start()=0
Start the rendering job and immediately return.
QPoint mouseLastXY()
returns last position of mouse cursor
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:111
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:168
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:42
double scale() const
Returns the last reported scale of the canvas.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:96
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
QColor canvasColor() const
Read property of QColor bgColor.
void clear()
Invalidates the cache contents, clearing all cached images.
Abstract base class for all map tools.
Definition: qgsmaptool.h:62
void selectionChangedSlot()
Receives signal about selection change, and pass it on with layer info.
Draw map such that there are no problems between adjacent tiles.
Job implementation that renders everything sequentially in one thread.
QgsUnitTypes::DistanceUnit mapUnits() const
Convience function for returning the current canvas map units.
void setBackgroundColor(const QColor &color)
Set the background color of the map.
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global configuration of the labeling engine.
void mouseReleaseEvent(QMouseEvent *e) override
Overridden mouse release event.
void writeProject(QDomDocument &)
emitted when project is being written
QgsPointXY toMapPoint(double x, double y) const
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:187
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:358
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:178
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:148
bool hasEntryForLayer(QgsMapLayer *layer) const
void dragEnterEvent(QDragEnterEvent *e) override
Overridden drag enter event.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:85
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
void writeProject(QDomDocument &)
called to write map canvas settings to project
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
void setLayers(const QList< QgsMapLayer *> &layers)
Sets the list of layers that should be shown in the canvas.
QgsRectangle fullExtent() const
returns current extent of layer set
Intermediate base class adding functionality that allows client to query the rendered image...
Stores global configuration for labeling engine.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:379
void zoomToFullExtent()
Zoom to the full extent of all layers.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
This class has all the configuration of snapping and can return answers to snapping queries...
const QgsLabelingResults * labelingResults() const
Get access to the labeling results (may be null)
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
void setMapSettingsFlags(QgsMapSettings::Flags flags)
Resets the flags for the canvas&#39; map settings.
void refreshAllLayers()
Reload all layers, clear the cache and refresh the canvas.
void zoomScale(double scale)
Zooms the canvas to a specific scale.
Class for doing transforms between two map coordinate systems.
void setExtent(const QgsRectangle &r, bool magnified=false)
Set the extent of the map canvas.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:101
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
const QgsMapToPixel * getCoordinateTransform()
Get the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
virtual void cancelWithoutBlocking()=0
Triggers cancelation of the rendering job without blocking.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:106
void setSelectionColor(const QColor &color)
Set color of selected vector features.
QString name
Definition: qgsmaplayer.h:58
void paintEvent(QPaintEvent *e) override
Overridden paint event.
void layerStateChange()
This slot is connected to the visibility change of one or more layers.
Enable vector simplification and other rendering optimizations.
void freeze(bool frozen=true)
Freeze/thaw the map canvas.
void setScaleLocked(bool isLocked)
Lock the scale, so zooming can be performed using magnication.
Class that stores computed placement from labeling engine.
This class is responsible for keeping cache of rendered images resulting from a map rendering job...
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:64
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:151
QList< int > QgsAttributeList
Definition: qgsfield.h:27
void readXml(QDomNode &node)
bool nextFeature(QgsFeature &f)
QPoint rubberStartPoint
Beginning point of a rubber band.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QgsSnappingUtils * snappingUtils() const
Return snapping utility class that is associated with map canvas.
virtual QgsLabelingResults * takeLabelingResults()=0
Get pointer to internal labeling engine (in order to get access to the results).
void xyCoordinates(const QgsPointXY &p)
Emits current mouse position.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void writeXml(QDomNode &node, QDomDocument &doc)
void zoomIn()
Zoom in with fixed factor.
virtual void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
virtual void updatePosition()
called on changed extent or resize event to update position of the item
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
const QgsMapSettings & mapSettings() const
Return map settings with which this job was started.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
CanvasProperties()=default
Constructor for CanvasProperties.
An interactive map canvas item which displays a QgsAnnotation.
void themeChanged(const QString &theme)
Emitted when the canvas has been assigned a different map theme.
void extentsChanged()
Emitted when the extents of the map change.
QgsAbstractGeometry * geometry() const
Returns the underlying geometry store.
QSize outputSize() const
Return the size of the resulting map image.
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsPointXY toMapCoordinates(int x, int y) const
QgsMapLayer * layer(int index)
return the map layer at position index in the layer stack
QString authid() const
Returns the authority identifier for the CRS.
virtual bool isActive() const =0
Tell whether the rendering job is currently running in background.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:70
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:130
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:158