QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsmapcanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 qgsmapcanvas.cpp - description
3 ------------------ -
4 begin : Sun Jun 30 2002
5 copyright : (C) 2002 by Gary E.Sherman
6 email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <cmath>
19 
20 #include <QtGlobal>
21 #include <QApplication>
22 #include <QCursor>
23 #include <QDir>
24 #include <QFile>
25 #include <QGraphicsItem>
26 #include <QGraphicsScene>
27 #include <QGraphicsView>
28 #include <QKeyEvent>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QPixmap>
32 #include <QRect>
33 #include <QTextStream>
34 #include <QResizeEvent>
35 #include <QScreen>
36 #include <QString>
37 #include <QStringList>
38 #include <QWheelEvent>
39 #include <QWindow>
40 
41 #include "qgis.h"
42 #include "qgssettings.h"
44 #include "qgsapplication.h"
45 #include "qgsexception.h"
47 #include "qgsfeatureiterator.h"
48 #include "qgslogger.h"
49 #include "qgsmapcanvas.h"
50 #include "qgsmapcanvasmap.h"
52 #include "qgsmaplayer.h"
53 #include "qgsmapmouseevent.h"
54 #include "qgsmaptoolpan.h"
55 #include "qgsmaptoolzoom.h"
56 #include "qgsmaptopixel.h"
57 #include "qgsmapoverviewcanvas.h"
58 #include "qgsmaprenderercache.h"
62 #include "qgsmapsettingsutils.h"
63 #include "qgsmessagelog.h"
64 #include "qgsmessageviewer.h"
65 #include "qgspallabeling.h"
66 #include "qgsproject.h"
67 #include "qgsrubberband.h"
68 #include "qgsvectorlayer.h"
69 #include "qgsmapthemecollection.h"
71 #include "qgssvgcache.h"
72 #include "qgsimagecache.h"
74 #include "qgsmimedatautils.h"
75 #include "qgscustomdrophandler.h"
76 #include "qgsreferencedgeometry.h"
77 
83 //TODO QGIS 4.0 - remove
85 {
86  public:
87 
91  CanvasProperties() = default;
92 
94  bool mouseButtonDown{ false };
95 
97  QPoint mouseLastXY;
98 
101 
103  bool panSelectorDown{ false };
104 };
105 
106 
107 
108 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
109  : QGraphicsView( parent )
111  , mExpressionContextScope( tr( "Map Canvas" ) )
112 {
113  mScene = new QGraphicsScene();
114  setScene( mScene );
115  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
116  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
117  setMouseTracking( true );
118  setFocusPolicy( Qt::StrongFocus );
119 
120  mResizeTimer = new QTimer( this );
121  mResizeTimer->setSingleShot( true );
122  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
123 
124  mRefreshTimer = new QTimer( this );
125  mRefreshTimer->setSingleShot( true );
126  connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
127 
128  // create map canvas item which will show the map
129  mMap = new QgsMapCanvasMap( this );
130 
131  // project handling
133  this, &QgsMapCanvas::readProject );
136 
137  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
138  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
139 
143  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
145  this, [ = ]
146  {
147  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
148  refresh();
149  } );
150  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
152  this, [ = ]
153  {
154  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
156  refresh();
157  } );
158 
159  // refresh canvas when a remote svg/image has finished downloading
162  // refresh canvas when project color scheme is changed -- if layers use project colors, they need to be redrawn
164 
165  //segmentation parameters
166  QgsSettings settings;
167  double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
168  QgsAbstractGeometry::SegmentationToleranceType toleranceType = settings.enumValue( QStringLiteral( "qgis/segmentationToleranceType" ), QgsAbstractGeometry::MaximumAngle );
169  mSettings.setSegmentationTolerance( segmentationTolerance );
170  mSettings.setSegmentationToleranceType( toleranceType );
171 
172  mWheelZoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
173 
174  QSize s = viewport()->size();
175  mSettings.setOutputSize( s );
176  mSettings.setDevicePixelRatio( devicePixelRatio() );
177  setSceneRect( 0, 0, s.width(), s.height() );
178  mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
179 
180  moveCanvasContents( true );
181 
182  // keep device pixel ratio up to date on screen or resolution change
183  if ( window()->windowHandle() )
184  {
185  connect( window()->windowHandle(), &QWindow::screenChanged, this, [ = ]( QScreen * ) {mSettings.setDevicePixelRatio( devicePixelRatio() );} );
186  connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, [ = ]( qreal ) {mSettings.setDevicePixelRatio( devicePixelRatio() );} );
187  }
188 
189  connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsMapCanvas::mapUpdateTimeout );
190  mMapUpdateTimer.setInterval( 250 );
191 
192 #ifdef Q_OS_WIN
193  // Enable touch event on Windows.
194  // Qt on Windows needs to be told it can take touch events or else it ignores them.
195  grabGesture( Qt::PinchGesture );
196  viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
197 #endif
198 
199  mPreviewEffect = new QgsPreviewEffect( this );
200  viewport()->setGraphicsEffect( mPreviewEffect );
201 
202  mZoomCursor = QgsApplication::getThemeCursor( QgsApplication::Cursor::ZoomIn );
203 
204  connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
205 
207 
208  setInteractive( false );
209 
210  // make sure we have the same default in QgsMapSettings and the scene's background brush
211  // (by default map settings has white bg color, scene background brush is black)
212  setCanvasColor( mSettings.backgroundColor() );
213 
214  refresh();
215 
216 } // QgsMapCanvas ctor
217 
218 
220 {
221  if ( mMapTool )
222  {
223  mMapTool->deactivate();
224  mMapTool = nullptr;
225  }
226  mLastNonZoomMapTool = nullptr;
227 
228  // rendering job may still end up writing into canvas map item
229  // so kill it before deleting canvas items
230  if ( mJob )
231  {
232  whileBlocking( mJob )->cancel();
233  delete mJob;
234  }
235 
236  QList< QgsMapRendererQImageJob * >::const_iterator previewJob = mPreviewJobs.constBegin();
237  for ( ; previewJob != mPreviewJobs.constEnd(); ++previewJob )
238  {
239  if ( *previewJob )
240  {
241  whileBlocking( *previewJob )->cancel();
242  delete *previewJob;
243  }
244  }
245 
246  // delete canvas items prior to deleting the canvas
247  // because they might try to update canvas when it's
248  // already being destructed, ends with segfault
249  qDeleteAll( mScene->items() );
250 
251  mScene->deleteLater(); // crashes in python tests on windows
252 
253  delete mCache;
254  delete mLabelingResults;
255 }
256 
258 {
259  // do not go higher or lower than min max magnification ratio
260  double magnifierMin = QgsGuiUtils::CANVAS_MAGNIFICATION_MIN;
261  double magnifierMax = QgsGuiUtils::CANVAS_MAGNIFICATION_MAX;
262  factor = qBound( magnifierMin, factor, magnifierMax );
263 
264  // the magnifier widget is in integer percent
265  if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
266  {
267  mSettings.setMagnificationFactor( factor );
268  refresh();
269  emit magnificationChanged( factor );
270  }
271 }
272 
274 {
275  return mSettings.magnificationFactor();
276 }
277 
279 {
280  mSettings.setFlag( QgsMapSettings::Antialiasing, flag );
281 } // anti aliasing
282 
284 {
285  mSettings.setFlag( QgsMapSettings::RenderMapTile, flag );
286 }
287 
289 {
290  QList<QgsMapLayer *> layers = mapSettings().layers();
291  if ( index >= 0 && index < layers.size() )
292  return layers[index];
293  else
294  return nullptr;
295 }
296 
298 {
299  if ( mCurrentLayer == layer )
300  return;
301 
302  mCurrentLayer = layer;
303  emit currentLayerChanged( layer );
304 }
305 
306 double QgsMapCanvas::scale() const
307 {
308  return mapSettings().scale();
309 }
310 
312 {
313  return nullptr != mJob;
314 } // isDrawing
315 
316 // return the current coordinate transform based on the extents and
317 // device size
319 {
320  return &mapSettings().mapToPixel();
321 }
322 
323 void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
324 {
325  // following a theme => request denied!
326  if ( !mTheme.isEmpty() )
327  return;
328 
329  setLayersPrivate( layers );
330 }
331 
332 void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
333 {
334  QList<QgsMapLayer *> oldLayers = mSettings.layers();
335 
336  // update only if needed
337  if ( layers == oldLayers )
338  return;
339 
340  const auto constOldLayers = oldLayers;
341  for ( QgsMapLayer *layer : constOldLayers )
342  {
343  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
344  disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
345  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
346  {
348  }
349  }
350 
351  mSettings.setLayers( layers );
352 
353  const auto constLayers = layers;
354  for ( QgsMapLayer *layer : constLayers )
355  {
356  if ( !layer )
357  continue;
358  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
359  connect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
360  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
361  {
363  }
364  }
365 
366  QgsDebugMsgLevel( QStringLiteral( "Layers have changed, refreshing" ), 2 );
367  emit layersChanged();
368 
369  updateAutoRefreshTimer();
370  refresh();
371 }
372 
373 
375 {
376  return mSettings;
377 }
378 
380 {
381  if ( mSettings.destinationCrs() == crs )
382  return;
383 
384  // try to reproject current extent to the new one
385  QgsRectangle rect;
386  if ( !mSettings.visibleExtent().isEmpty() )
387  {
388  QgsCoordinateTransform transform( mSettings.destinationCrs(), crs, QgsProject::instance() );
389  try
390  {
391  rect = transform.transformBoundingBox( mSettings.visibleExtent() );
392  }
393  catch ( QgsCsException &e )
394  {
395  Q_UNUSED( e )
396  QgsDebugMsg( QStringLiteral( "Transform error caught: %1" ).arg( e.what() ) );
397  }
398  }
399 
400  if ( !rect.isEmpty() )
401  {
402  setExtent( rect );
403  }
404 
405  mSettings.setDestinationCrs( crs );
406  updateScale();
407 
408  QgsDebugMsgLevel( QStringLiteral( "refreshing after destination CRS changed" ), 2 );
409  refresh();
410 
411  emit destinationCrsChanged();
412 }
413 
414 void QgsMapCanvas::setMapSettingsFlags( QgsMapSettings::Flags flags )
415 {
416  mSettings.setFlags( flags );
417  clearCache();
418  refresh();
419 }
420 
422 {
423  return mLabelingResults;
424 }
425 
427 {
428  if ( enabled == isCachingEnabled() )
429  return;
430 
431  if ( mJob && mJob->isActive() )
432  {
433  // wait for the current rendering to finish, before touching the cache
434  mJob->waitForFinished();
435  }
436 
437  if ( enabled )
438  {
439  mCache = new QgsMapRendererCache;
440  }
441  else
442  {
443  delete mCache;
444  mCache = nullptr;
445  }
446 }
447 
449 {
450  return nullptr != mCache;
451 }
452 
454 {
455  if ( mCache )
456  mCache->clear();
457 }
458 
460 {
461  mUseParallelRendering = enabled;
462 }
463 
465 {
466  return mUseParallelRendering;
467 }
468 
469 void QgsMapCanvas::setMapUpdateInterval( int timeMilliseconds )
470 {
471  mMapUpdateTimer.setInterval( timeMilliseconds );
472 }
473 
475 {
476  return mMapUpdateTimer.interval();
477 }
478 
479 
481 {
482  return mCurrentLayer;
483 }
484 
486 {
487  QgsExpressionContextScope *s = new QgsExpressionContextScope( QObject::tr( "Map Canvas" ) );
488  s->setVariable( QStringLiteral( "canvas_cursor_point" ), QgsGeometry::fromPointXY( cursorPoint() ), true );
489 
490  return s;
491 }
492 
494 {
495  if ( !mSettings.hasValidSettings() )
496  {
497  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh - invalid settings -> nothing to do" ), 2 );
498  return;
499  }
500 
501  if ( !mRenderFlag || mFrozen )
502  {
503  QgsDebugMsgLevel( QStringLiteral( "CANVAS render flag off" ), 2 );
504  return;
505  }
506 
507  if ( mRefreshScheduled )
508  {
509  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh already scheduled" ), 2 );
510  return;
511  }
512 
513  mRefreshScheduled = true;
514 
515  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh scheduling" ), 2 );
516 
517  // schedule a refresh
518  mRefreshTimer->start( 1 );
519 }
520 
521 void QgsMapCanvas::refreshMap()
522 {
523  Q_ASSERT( mRefreshScheduled );
524 
525  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh!" ), 3 );
526 
527  stopRendering(); // if any...
528  stopPreviewJobs();
529 
530  //build the expression context
531  QgsExpressionContext expressionContext;
532  expressionContext << QgsExpressionContextUtils::globalScope()
537  << new QgsExpressionContextScope( mExpressionContextScope );
538 
539  mSettings.setExpressionContext( expressionContext );
540  mSettings.setPathResolver( QgsProject::instance()->pathResolver() );
541 
542  if ( !mTheme.isEmpty() )
543  {
544  // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
545  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
546  // current state of the style. If we had stored the style overrides earlier (such as in
547  // mapThemeChanged slot) then this xml could be out of date...
548  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
549  // just return the style name, we can instead set the overrides in mapThemeChanged and not here
550  mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
551  }
552 
553  // create the renderer job
554  Q_ASSERT( !mJob );
555  mJobCanceled = false;
556  if ( mUseParallelRendering )
557  mJob = new QgsMapRendererParallelJob( mSettings );
558  else
559  mJob = new QgsMapRendererSequentialJob( mSettings );
560  connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
561  mJob->setCache( mCache );
562 
563  mJob->start();
564 
565  // from now on we can accept refresh requests again
566  // this must be reset only after the job has been started, because
567  // some providers (yes, it's you WCS and AMS!) during preparation
568  // do network requests and start an internal event loop, which may
569  // end up calling refresh() and would schedule another refresh,
570  // deleting the one we have just started.
571  mRefreshScheduled = false;
572 
573  mMapUpdateTimer.start();
574 
575  emit renderStarting();
576 }
577 
578 void QgsMapCanvas::mapThemeChanged( const QString &theme )
579 {
580  if ( theme == mTheme )
581  {
582  // set the canvas layers to match the new layers contained in the map theme
583  // NOTE: we do this when the theme layers change and not when we are refreshing the map
584  // as setLayers() sets up necessary connections to handle changes to the layers
585  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
586  // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
587  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
588  // current state of the style. If changes were made to the style then this xml
589  // snapshot goes out of sync...
590  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
591  // just return the style name, we can instead set the overrides here and not in refreshMap()
592 
593  clearCache();
594  refresh();
595  }
596 }
597 
598 void QgsMapCanvas::rendererJobFinished()
599 {
600  QgsDebugMsgLevel( QStringLiteral( "CANVAS finish! %1" ).arg( !mJobCanceled ), 2 );
601 
602  mMapUpdateTimer.stop();
603 
604  // TODO: would be better to show the errors in message bar
605  const auto constErrors = mJob->errors();
606  for ( const QgsMapRendererJob::Error &error : constErrors )
607  {
608  QgsMapLayer *layer = QgsProject::instance()->mapLayer( error.layerID );
609  emit renderErrorOccurred( error.message, layer );
610  QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) );
611  }
612 
613  if ( !mJobCanceled )
614  {
615  // take labeling results before emitting renderComplete, so labeling map tools
616  // connected to signal work with correct results
617  if ( !mJob->usedCachedLabels() )
618  {
619  delete mLabelingResults;
620  mLabelingResults = mJob->takeLabelingResults();
621  }
622 
623  QImage img = mJob->renderedImage();
624 
625  // emit renderComplete to get our decorations drawn
626  QPainter p( &img );
627  emit renderComplete( &p );
628 
629  QgsSettings settings;
630  if ( settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
631  {
632  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
633  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
634  }
635 
636  if ( mDrawRenderingStats )
637  {
638  int w = img.width(), h = img.height();
639  QFont fnt = p.font();
640  fnt.setBold( true );
641  p.setFont( fnt );
642  int lh = p.fontMetrics().height() * 2;
643  QRect r( 0, h - lh, w, lh );
644  p.setPen( Qt::NoPen );
645  p.setBrush( QColor( 0, 0, 0, 110 ) );
646  p.drawRect( r );
647  p.setPen( Qt::white );
648  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? QStringLiteral( "PARALLEL" ) : QStringLiteral( "SEQUENTIAL" ) ).arg( mJob->renderingTime() );
649  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
650  }
651 
652  p.end();
653 
654  mMap->setContent( img, imageRect( img, mSettings ) );
655 
656  mLastLayerRenderTime.clear();
657  const auto times = mJob->perLayerRenderingTime();
658  for ( auto it = times.constBegin(); it != times.constEnd(); ++it )
659  {
660  mLastLayerRenderTime.insert( it.key()->id(), it.value() );
661  }
662  if ( mUsePreviewJobs )
663  startPreviewJobs();
664  }
665 
666  // now we are in a slot called from mJob - do not delete it immediately
667  // so the class is still valid when the execution returns to the class
668  mJob->deleteLater();
669  mJob = nullptr;
670 
671  emit mapCanvasRefreshed();
672 }
673 
674 void QgsMapCanvas::previewJobFinished()
675 {
676  QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
677  Q_ASSERT( job );
678 
679  if ( mMap )
680  {
681  mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
682  mPreviewJobs.removeAll( job );
683 
684  int number = job->property( "number" ).toInt();
685  if ( number < 8 )
686  {
687  startPreviewJob( number + 1 );
688  }
689 
690  delete job;
691  }
692 }
693 
694 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
695 {
696  // This is a hack to pass QgsMapCanvasItem::setRect what it
697  // expects (encoding of position and size of the item)
698  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
699  QgsPointXY topLeft = m2p.toMapCoordinates( 0, 0 );
700 #ifdef QGISDEBUG
701  // do not assert this, since it might lead to crashes when changing screen while rendering
702  if ( img.devicePixelRatio() != mapSettings.devicePixelRatio() )
703  {
704  QgsLogger::warning( QStringLiteral( "The renderer map has a wrong device pixel ratio" ) );
705  }
706 #endif
707  double res = m2p.mapUnitsPerPixel() / img.devicePixelRatioF();
708  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
709  return rect;
710 }
711 
713 {
714  return mUsePreviewJobs;
715 }
716 
718 {
719  mUsePreviewJobs = enabled;
720 }
721 
722 void QgsMapCanvas::setCustomDropHandlers( const QVector<QPointer<QgsCustomDropHandler> > &handlers )
723 {
724  mDropHandlers = handlers;
725 }
726 
727 void QgsMapCanvas::mapUpdateTimeout()
728 {
729  if ( mJob )
730  {
731  const QImage &img = mJob->renderedImage();
732  mMap->setContent( img, imageRect( img, mSettings ) );
733  }
734 }
735 
737 {
738  if ( mJob )
739  {
740  QgsDebugMsgLevel( QStringLiteral( "CANVAS stop rendering!" ), 2 );
741  mJobCanceled = true;
742  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
743  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
744  mJob->cancelWithoutBlocking();
745  mJob = nullptr;
746  }
747  stopPreviewJobs();
748 }
749 
750 //the format defaults to "PNG" if not specified
751 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
752 {
753  QPainter painter;
754  QImage image;
755 
756  //
757  //check if the optional QPaintDevice was supplied
758  //
759  if ( theQPixmap )
760  {
761  image = theQPixmap->toImage();
762  painter.begin( &image );
763 
764  // render
765  QgsMapRendererCustomPainterJob job( mSettings, &painter );
766  job.start();
767  job.waitForFinished();
768  emit renderComplete( &painter );
769  }
770  else //use the map view
771  {
772  image = mMap->contentImage().copy();
773  painter.begin( &image );
774  }
775 
776  // draw annotations
777  QStyleOptionGraphicsItem option;
778  option.initFrom( this );
779  QGraphicsItem *item = nullptr;
780  QListIterator<QGraphicsItem *> i( items() );
781  i.toBack();
782  while ( i.hasPrevious() )
783  {
784  item = i.previous();
785 
786  if ( !( item && dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) ) )
787  {
788  continue;
789  }
790 
791  painter.save();
792 
793  QPointF itemScenePos = item->scenePos();
794  painter.translate( itemScenePos.x(), itemScenePos.y() );
795 
796  item->paint( &painter, &option );
797 
798  painter.restore();
799  }
800 
801  painter.end();
802  image.save( fileName, format.toLocal8Bit().data() );
803 
804  QFileInfo myInfo = QFileInfo( fileName );
805 
806  // build the world file name
807  QString outputSuffix = myInfo.suffix();
808  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
809  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
810  QFile myWorldFile( myWorldFileName );
811  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
812  {
813  return;
814  }
815  QTextStream myStream( &myWorldFile );
817 } // saveAsImage
818 
819 
820 
822 {
823  return mapSettings().visibleExtent();
824 } // extent
825 
827 {
828  return mapSettings().fullExtent();
829 } // extent
830 
831 
832 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
833 {
834  QgsRectangle current = extent();
835 
836  if ( ( r == current ) && magnified )
837  return;
838 
839  if ( r.isEmpty() )
840  {
841  if ( !mSettings.hasValidSettings() )
842  {
843  // we can't even just move the map center
844  QgsDebugMsgLevel( QStringLiteral( "Empty extent - ignoring" ), 2 );
845  return;
846  }
847 
848  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
849  QgsDebugMsgLevel( QStringLiteral( "Empty extent - keeping old scale with new center!" ), 2 );
850  setCenter( r.center() );
851  }
852  else
853  {
854  mSettings.setExtent( r, magnified );
855  }
856  emit extentsChanged();
857  updateScale();
858  if ( mLastExtent.size() > 20 )
859  mLastExtent.removeAt( 0 );
860 
861  //clear all extent items after current index
862  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
863  {
864  mLastExtent.removeAt( i );
865  }
866 
867  mLastExtent.append( extent() );
868 
869  // adjust history to no more than 20
870  if ( mLastExtent.size() > 20 )
871  {
872  mLastExtent.removeAt( 0 );
873  }
874 
875  // the last item is the current extent
876  mLastExtentIndex = mLastExtent.size() - 1;
877 
878  // update controls' enabled state
879  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
880  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
881 }
882 
884 {
885  QgsRectangle canvasExtent = extent;
886  if ( extent.crs() != mapSettings().destinationCrs() )
887  {
889  canvasExtent = ct.transform( extent );
890 
891  if ( canvasExtent.isEmpty() )
892  {
893  return false;
894  }
895  }
896 
897  setExtent( canvasExtent );
898  return true;
899 }
900 
902 {
904  double x = center.x();
905  double y = center.y();
906  setExtent(
907  QgsRectangle(
908  x - r.width() / 2.0, y - r.height() / 2.0,
909  x + r.width() / 2.0, y + r.height() / 2.0
910  ),
911  true
912  );
913 } // setCenter
914 
916 {
918  return r.center();
919 }
920 
921 QgsPointXY QgsMapCanvas::cursorPoint() const
922 {
923  return mCursorPoint;
924 }
925 
927 {
928  return mapSettings().rotation();
929 } // rotation
930 
931 void QgsMapCanvas::setRotation( double degrees )
932 {
933  double current = rotation();
934 
935  if ( qgsDoubleNear( degrees, current ) )
936  return;
937 
938  mSettings.setRotation( degrees );
939  emit rotationChanged( degrees );
940  emit extentsChanged(); // visible extent changes with rotation
941 } // setRotation
942 
943 
945 {
946  emit scaleChanged( mapSettings().scale() );
947 }
948 
949 
951 {
953  // If the full extent is an empty set, don't do the zoom
954  if ( !extent.isEmpty() )
955  {
956  // Add a 5% margin around the full extent
957  extent.scale( 1.05 );
958  setExtent( extent );
959  }
960  refresh();
961 
962 } // zoomToFullExtent
963 
964 
966 {
967  if ( mLastExtentIndex > 0 )
968  {
969  mLastExtentIndex--;
970  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
971  emit extentsChanged();
972  updateScale();
973  refresh();
974  // update controls' enabled state
975  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
976  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
977  }
978 
979 } // zoomToPreviousExtent
980 
982 {
983  if ( mLastExtentIndex < mLastExtent.size() - 1 )
984  {
985  mLastExtentIndex++;
986  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
987  emit extentsChanged();
988  updateScale();
989  refresh();
990  // update controls' enabled state
991  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
992  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
993  }
994 }// zoomToNextExtent
995 
997 {
998  mLastExtent.clear(); // clear the zoom history list
999  mLastExtent.append( extent() ) ; // set the current extent in the list
1000  mLastExtentIndex = mLastExtent.size() - 1;
1001  // update controls' enabled state
1002  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1003  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1004 }// clearExtentHistory
1005 
1007 {
1008  if ( !layer )
1009  {
1010  // use current layer by default
1011  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1012  }
1013 
1014  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1015  return;
1016 
1017  QgsRectangle rect = layer->boundingBoxOfSelected();
1018  if ( rect.isNull() )
1019  {
1020  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
1021  return;
1022  }
1023 
1024  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1025 
1026  // zoom in if point cannot be distinguished from others
1027  // also check that rect is empty, as it might not in case of multi points
1028  if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1029  {
1030  int scaleFactor = 5;
1031  QgsPointXY center = mSettings.mapToLayerCoordinates( layer, rect.center() );
1032  QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, &center );
1034  QgsFeatureIterator fit = layer->getFeatures( req );
1035  QgsFeature f;
1036  QgsPointXY closestPoint;
1037  double closestSquaredDistance = pow( extentRect.width(), 2.0 ) + pow( extentRect.height(), 2.0 );
1038  bool pointFound = false;
1039  while ( fit.nextFeature( f ) )
1040  {
1041  QgsPointXY point = f.geometry().asPoint();
1042  double sqrDist = point.sqrDist( center );
1043  if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits<double>::epsilon() )
1044  continue;
1045  pointFound = true;
1046  closestPoint = point;
1047  closestSquaredDistance = sqrDist;
1048  }
1049  if ( pointFound )
1050  {
1051  // combine selected point with closest point and scale this rect
1052  rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) );
1053  rect.scale( scaleFactor, &center );
1054  }
1055  }
1056 
1057  zoomToFeatureExtent( rect );
1058 }
1059 
1061 {
1062  // no selected features, only one selected point feature
1063  //or two point features with the same x- or y-coordinates
1064  if ( rect.isEmpty() )
1065  {
1066  // zoom in
1067  QgsPointXY c = rect.center();
1068  rect = extent();
1069  rect.scale( 1.0, &c );
1070  }
1071  //zoom to an area
1072  else
1073  {
1074  // Expand rect to give a bit of space around the selected
1075  // objects so as to keep them clear of the map boundaries
1076  // The same 5% should apply to all margins.
1077  rect.scale( 1.05 );
1078  }
1079 
1080  setExtent( rect );
1081  refresh();
1082 }
1083 
1085 {
1086  if ( !layer )
1087  {
1088  return;
1089  }
1090 
1091  QgsRectangle bbox;
1092  QString errorMsg;
1093  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1094  {
1095  zoomToFeatureExtent( bbox );
1096  }
1097  else
1098  {
1099  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, Qgis::Warning );
1100  }
1101 
1102 }
1103 
1104 void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter )
1105 {
1106  if ( !layer )
1107  {
1108  return;
1109  }
1110 
1111  QgsRectangle bbox;
1112  QString errorMsg;
1113  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1114  {
1115  if ( alwaysRecenter || !mapSettings().extent().contains( bbox ) )
1116  setCenter( bbox.center() );
1117  refresh();
1118  }
1119  else
1120  {
1121  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, Qgis::Warning );
1122  }
1123 }
1124 
1125 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1126 {
1127  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1128  bbox.setMinimal();
1129  QgsFeature fet;
1130  int featureCount = 0;
1131  errorMsg.clear();
1132 
1133  while ( it.nextFeature( fet ) )
1134  {
1135  QgsGeometry geom = fet.geometry();
1136  if ( geom.isNull() )
1137  {
1138  errorMsg = tr( "Feature does not have a geometry" );
1139  }
1140  else if ( geom.constGet()->isEmpty() )
1141  {
1142  errorMsg = tr( "Feature geometry is empty" );
1143  }
1144  if ( !errorMsg.isEmpty() )
1145  {
1146  return false;
1147  }
1149  bbox.combineExtentWith( r );
1150  featureCount++;
1151  }
1152 
1153  if ( featureCount != ids.count() )
1154  {
1155  errorMsg = tr( "Feature not found" );
1156  return false;
1157  }
1158 
1159  return true;
1160 }
1161 
1163 {
1164  if ( !layer )
1165  {
1166  // use current layer by default
1167  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1168  }
1169 
1170  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1171  return;
1172 
1173  QgsRectangle rect = layer->boundingBoxOfSelected();
1174  if ( rect.isNull() )
1175  {
1176  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::Warning );
1177  return;
1178  }
1179 
1180  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1181  setCenter( rect.center() );
1182  refresh();
1183 }
1184 
1186  const QColor &color1, const QColor &color2,
1187  int flashes, int duration )
1188 {
1189  if ( !layer )
1190  {
1191  return;
1192  }
1193 
1194  QList< QgsGeometry > geoms;
1195 
1196  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1197  QgsFeature fet;
1198  while ( it.nextFeature( fet ) )
1199  {
1200  if ( !fet.hasGeometry() )
1201  continue;
1202  geoms << fet.geometry();
1203  }
1204 
1205  flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
1206 }
1207 
1208 void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
1209 {
1210  if ( geometries.isEmpty() )
1211  return;
1212 
1213  QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
1214  QgsRubberBand *rb = new QgsRubberBand( this, geomType );
1215  for ( const QgsGeometry &geom : geometries )
1216  rb->addGeometry( geom, crs );
1217 
1218  if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
1219  {
1220  rb->setWidth( 2 );
1221  rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
1222  }
1223  if ( geomType == QgsWkbTypes::PointGeometry )
1224  rb->setIcon( QgsRubberBand::ICON_CIRCLE );
1225 
1226  QColor startColor = color1;
1227  if ( !startColor.isValid() )
1228  {
1229  if ( geomType == QgsWkbTypes::PolygonGeometry )
1230  {
1231  startColor = rb->fillColor();
1232  }
1233  else
1234  {
1235  startColor = rb->strokeColor();
1236  }
1237  startColor.setAlpha( 255 );
1238  }
1239  QColor endColor = color2;
1240  if ( !endColor.isValid() )
1241  {
1242  endColor = startColor;
1243  endColor.setAlpha( 0 );
1244  }
1245 
1246 
1247  QVariantAnimation *animation = new QVariantAnimation( this );
1248  connect( animation, &QVariantAnimation::finished, this, [animation, rb]
1249  {
1250  animation->deleteLater();
1251  delete rb;
1252  } );
1253  connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
1254  {
1255  QColor c = value.value<QColor>();
1256  if ( geomType == QgsWkbTypes::PolygonGeometry )
1257  {
1258  rb->setFillColor( c );
1259  }
1260  else
1261  {
1262  rb->setStrokeColor( c );
1263  QColor c = rb->secondaryStrokeColor();
1264  c.setAlpha( c.alpha() );
1265  rb->setSecondaryStrokeColor( c );
1266  }
1267  rb->update();
1268  } );
1269 
1270  animation->setDuration( duration * flashes );
1271  animation->setStartValue( endColor );
1272  double midStep = 0.2 / flashes;
1273  for ( int i = 0; i < flashes; ++i )
1274  {
1275  double start = static_cast< double >( i ) / flashes;
1276  animation->setKeyValueAt( start + midStep, startColor );
1277  double end = static_cast< double >( i + 1 ) / flashes;
1278  if ( !qgsDoubleNear( end, 1.0 ) )
1279  animation->setKeyValueAt( end, endColor );
1280  }
1281  animation->setEndValue( endColor );
1282  animation->start();
1283 }
1284 
1285 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1286 {
1287  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1288  {
1289  emit keyPressed( e );
1290  return;
1291  }
1292 
1293  if ( ! mCanvasProperties->mouseButtonDown )
1294  {
1295  // Don't want to interfer with mouse events
1296 
1297  QgsRectangle currentExtent = mapSettings().visibleExtent();
1298  double dx = std::fabs( currentExtent.width() / 4 );
1299  double dy = std::fabs( currentExtent.height() / 4 );
1300 
1301  switch ( e->key() )
1302  {
1303  case Qt::Key_Left:
1304  QgsDebugMsgLevel( QStringLiteral( "Pan left" ), 2 );
1305  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1306  refresh();
1307  break;
1308 
1309  case Qt::Key_Right:
1310  QgsDebugMsgLevel( QStringLiteral( "Pan right" ), 2 );
1311  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1312  refresh();
1313  break;
1314 
1315  case Qt::Key_Up:
1316  QgsDebugMsgLevel( QStringLiteral( "Pan up" ), 2 );
1317  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1318  refresh();
1319  break;
1320 
1321  case Qt::Key_Down:
1322  QgsDebugMsgLevel( QStringLiteral( "Pan down" ), 2 );
1323  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1324  refresh();
1325  break;
1326 
1327 
1328 
1329  case Qt::Key_Space:
1330  QgsDebugMsgLevel( QStringLiteral( "Pressing pan selector" ), 2 );
1331 
1332  //mCanvasProperties->dragging = true;
1333  if ( ! e->isAutoRepeat() )
1334  {
1335  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1336  mCanvasProperties->panSelectorDown = true;
1337  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1338  }
1339  break;
1340 
1341  case Qt::Key_PageUp:
1342  QgsDebugMsgLevel( QStringLiteral( "Zoom in" ), 2 );
1343  zoomIn();
1344  break;
1345 
1346  case Qt::Key_PageDown:
1347  QgsDebugMsgLevel( QStringLiteral( "Zoom out" ), 2 );
1348  zoomOut();
1349  break;
1350 
1351 #if 0
1352  case Qt::Key_P:
1353  mUseParallelRendering = !mUseParallelRendering;
1354  refresh();
1355  break;
1356 
1357  case Qt::Key_S:
1358  mDrawRenderingStats = !mDrawRenderingStats;
1359  refresh();
1360  break;
1361 #endif
1362 
1363  default:
1364  // Pass it on
1365  if ( mMapTool )
1366  {
1367  mMapTool->keyPressEvent( e );
1368  }
1369  else e->ignore();
1370 
1371  QgsDebugMsgLevel( "Ignoring key: " + QString::number( e->key() ), 2 );
1372  }
1373  }
1374 
1375  emit keyPressed( e );
1376 
1377 } //keyPressEvent()
1378 
1379 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1380 {
1381  QgsDebugMsgLevel( QStringLiteral( "keyRelease event" ), 2 );
1382 
1383  switch ( e->key() )
1384  {
1385  case Qt::Key_Space:
1386  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1387  {
1388  QgsDebugMsgLevel( QStringLiteral( "Releasing pan selector" ), 2 );
1389  QApplication::restoreOverrideCursor();
1390  mCanvasProperties->panSelectorDown = false;
1391  panActionEnd( mCanvasProperties->mouseLastXY );
1392  }
1393  break;
1394 
1395  default:
1396  // Pass it on
1397  if ( mMapTool )
1398  {
1399  mMapTool->keyReleaseEvent( e );
1400  }
1401  else e->ignore();
1402 
1403  QgsDebugMsgLevel( "Ignoring key release: " + QString::number( e->key() ), 2 );
1404  }
1405 
1406  emit keyReleased( e );
1407 
1408 } //keyReleaseEvent()
1409 
1410 
1412 {
1413  // call handler of current map tool
1414  if ( mMapTool )
1415  {
1416  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1417  mMapTool->canvasDoubleClickEvent( me.get() );
1418  }
1419 }// mouseDoubleClickEvent
1420 
1421 
1422 void QgsMapCanvas::beginZoomRect( QPoint pos )
1423 {
1424  mZoomRect.setRect( 0, 0, 0, 0 );
1425  QApplication::setOverrideCursor( mZoomCursor );
1426  mZoomDragging = true;
1427  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1428  QColor color( Qt::blue );
1429  color.setAlpha( 63 );
1430  mZoomRubberBand->setColor( color );
1431  mZoomRect.setTopLeft( pos );
1432 }
1433 
1434 void QgsMapCanvas::endZoomRect( QPoint pos )
1435 {
1436  mZoomDragging = false;
1437  mZoomRubberBand.reset( nullptr );
1438  QApplication::restoreOverrideCursor();
1439 
1440  // store the rectangle
1441  mZoomRect.setRight( pos.x() );
1442  mZoomRect.setBottom( pos.y() );
1443 
1444  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1445  {
1446  //probably a mistake - would result in huge zoom!
1447  return;
1448  }
1449 
1450  //account for bottom right -> top left dragging
1451  mZoomRect = mZoomRect.normalized();
1452 
1453  // set center and zoom
1454  const QSize &zoomRectSize = mZoomRect.size();
1455  const QSize &canvasSize = mSettings.outputSize();
1456  double sfx = static_cast< double >( zoomRectSize.width() ) / canvasSize.width();
1457  double sfy = static_cast< double >( zoomRectSize.height() ) / canvasSize.height();
1458  double sf = std::max( sfx, sfy );
1459 
1460  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1461 
1462  zoomByFactor( sf, &c );
1463  refresh();
1464 }
1465 
1466 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1467 {
1468  //use middle mouse button for panning, map tools won't receive any events in that case
1469  if ( e->button() == Qt::MidButton )
1470  {
1471  mCanvasProperties->panSelectorDown = true;
1472  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1473  }
1474  else
1475  {
1476  // call handler of current map tool
1477  if ( mMapTool )
1478  {
1479  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1480  && e->modifiers() & Qt::ShiftModifier )
1481  {
1482  beginZoomRect( e->pos() );
1483  return;
1484  }
1485  else
1486  {
1487  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1488  mMapTool->canvasPressEvent( me.get() );
1489  }
1490  }
1491  }
1492 
1493  if ( mCanvasProperties->panSelectorDown )
1494  {
1495  return;
1496  }
1497 
1498  mCanvasProperties->mouseButtonDown = true;
1499  mCanvasProperties->rubberStartPoint = e->pos();
1500 
1501 } // mousePressEvent
1502 
1503 
1504 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1505 {
1506  //use middle mouse button for panning, map tools won't receive any events in that case
1507  if ( e->button() == Qt::MidButton )
1508  {
1509  mCanvasProperties->panSelectorDown = false;
1510  panActionEnd( mCanvasProperties->mouseLastXY );
1511  }
1512  else if ( e->button() == Qt::BackButton )
1513  {
1515  return;
1516  }
1517  else if ( e->button() == Qt::ForwardButton )
1518  {
1519  zoomToNextExtent();
1520  return;
1521  }
1522  else
1523  {
1524  if ( mZoomDragging && e->button() == Qt::LeftButton )
1525  {
1526  endZoomRect( e->pos() );
1527  return;
1528  }
1529 
1530  // call handler of current map tool
1531  if ( mMapTool )
1532  {
1533  // right button was pressed in zoom tool? return to previous non zoom tool
1534  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1535  {
1536  QgsDebugMsgLevel( QStringLiteral( "Right click in map tool zoom or pan, last tool is %1." ).arg(
1537  mLastNonZoomMapTool ? QStringLiteral( "not null" ) : QStringLiteral( "null" ) ), 2 );
1538 
1539  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1540 
1541  // change to older non-zoom tool
1542  if ( mLastNonZoomMapTool
1543  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1544  || ( vlayer && vlayer->isEditable() ) ) )
1545  {
1546  QgsMapTool *t = mLastNonZoomMapTool;
1547  mLastNonZoomMapTool = nullptr;
1548  setMapTool( t );
1549  }
1550  return;
1551  }
1552  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1553  mMapTool->canvasReleaseEvent( me.get() );
1554  }
1555  }
1556 
1557 
1558  mCanvasProperties->mouseButtonDown = false;
1559 
1560  if ( mCanvasProperties->panSelectorDown )
1561  return;
1562 
1563 } // mouseReleaseEvent
1564 
1565 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1566 {
1567  QGraphicsView::resizeEvent( e );
1568  mResizeTimer->start( 500 ); // in charge of refreshing canvas
1569 
1570  double oldScale = mSettings.scale();
1571  QSize lastSize = viewport()->size();
1572  mSettings.setOutputSize( lastSize );
1573 
1574  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1575 
1576  moveCanvasContents( true );
1577 
1578  if ( mScaleLocked )
1579  {
1580  double scaleFactor = oldScale / mSettings.scale();
1581  QgsRectangle r = mSettings.extent();
1582  QgsPointXY center = r.center();
1583  r.scale( scaleFactor, &center );
1584  mSettings.setExtent( r );
1585  }
1586  else
1587  {
1588  updateScale();
1589  }
1590 
1591  emit extentsChanged();
1592 }
1593 
1594 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1595 {
1596  // no custom event handling anymore
1597 
1598  QGraphicsView::paintEvent( e );
1599 } // paintEvent
1600 
1602 {
1603  const QList<QGraphicsItem *> items = mScene->items();
1604  for ( QGraphicsItem *gi : items )
1605  {
1606  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( gi );
1607 
1608  if ( item )
1609  {
1610  item->updatePosition();
1611  }
1612  }
1613 }
1614 
1615 
1616 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1617 {
1618  // Zoom the map canvas in response to a mouse wheel event. Moving the
1619  // wheel forward (away) from the user zooms in
1620 
1621  QgsDebugMsgLevel( "Wheel event delta " + QString::number( e->delta() ), 2 );
1622 
1623  if ( mMapTool )
1624  {
1625  mMapTool->wheelEvent( e );
1626  if ( e->isAccepted() )
1627  return;
1628  }
1629 
1630  if ( e->delta() == 0 )
1631  {
1632  e->accept();
1633  return;
1634  }
1635 
1636  double zoomFactor = mWheelZoomFactor;
1637 
1638  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1639  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
1640 
1641  if ( e->modifiers() & Qt::ControlModifier )
1642  {
1643  //holding ctrl while wheel zooming results in a finer zoom
1644  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1645  }
1646 
1647  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1648 
1649  // zoom map to mouse cursor by scaling
1650  QgsPointXY oldCenter = center();
1651  QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->x(), e->y() ) );
1652  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1653  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1654 
1655  zoomByFactor( signedWheelFactor, &newCenter );
1656  e->accept();
1657 }
1658 
1659 void QgsMapCanvas::setWheelFactor( double factor )
1660 {
1661  mWheelZoomFactor = factor;
1662 }
1663 
1665 {
1666  // magnification is alreday handled in zoomByFactor
1667  zoomByFactor( 1 / mWheelZoomFactor );
1668 }
1669 
1671 {
1672  // magnification is alreday handled in zoomByFactor
1673  zoomByFactor( mWheelZoomFactor );
1674 }
1675 
1676 void QgsMapCanvas::zoomScale( double newScale )
1677 {
1678  zoomByFactor( newScale / scale() );
1679 }
1680 
1681 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1682 {
1683  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1684 
1685  if ( mScaleLocked )
1686  {
1688  }
1689  else
1690  {
1691  // transform the mouse pos to map coordinates
1694  r.scale( scaleFactor, &center );
1695  setExtent( r, true );
1696  refresh();
1697  }
1698 }
1699 
1700 void QgsMapCanvas::setScaleLocked( bool isLocked )
1701 {
1702  mScaleLocked = isLocked;
1703 }
1704 
1705 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1706 {
1707  mCanvasProperties->mouseLastXY = e->pos();
1708 
1709  if ( mCanvasProperties->panSelectorDown )
1710  {
1711  panAction( e );
1712  }
1713  else if ( mZoomDragging )
1714  {
1715  mZoomRect.setBottomRight( e->pos() );
1716  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1717  mZoomRubberBand->show();
1718  }
1719  else
1720  {
1721  // call handler of current map tool
1722  if ( mMapTool )
1723  {
1724  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1725  mMapTool->canvasMoveEvent( me.get() );
1726  }
1727  }
1728 
1729  // show x y on status bar
1730  mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
1731  emit xyCoordinates( mCursorPoint );
1732 }
1733 
1734 void QgsMapCanvas::setMapTool( QgsMapTool *tool, bool clean )
1735 {
1736  if ( !tool )
1737  return;
1738 
1739  if ( mMapTool )
1740  {
1741  if ( clean )
1742  mMapTool->clean();
1743 
1744  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1745  mMapTool->deactivate();
1746  }
1747 
1748  if ( ( tool->flags() & QgsMapTool::Transient )
1749  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1750  {
1751  // if zoom or pan tool will be active, save old tool
1752  // to bring it back on right click
1753  // (but only if it wasn't also zoom or pan tool)
1754  mLastNonZoomMapTool = mMapTool;
1755  }
1756  else
1757  {
1758  mLastNonZoomMapTool = nullptr;
1759  }
1760 
1761  QgsMapTool *oldTool = mMapTool;
1762 
1763  // set new map tool and activate it
1764  mMapTool = tool;
1765  if ( mMapTool )
1766  {
1767  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1768  mMapTool->activate();
1769  }
1770 
1771  emit mapToolSet( mMapTool, oldTool );
1772 } // setMapTool
1773 
1775 {
1776  if ( mMapTool && mMapTool == tool )
1777  {
1778  mMapTool->deactivate();
1779  mMapTool = nullptr;
1780  emit mapToolSet( nullptr, mMapTool );
1781  setCursor( Qt::ArrowCursor );
1782  }
1783 
1784  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1785  {
1786  mLastNonZoomMapTool = nullptr;
1787  }
1788 }
1789 
1790 void QgsMapCanvas::setCanvasColor( const QColor &color )
1791 {
1792  if ( canvasColor() == color )
1793  return;
1794 
1795  // background of map's pixmap
1796  mSettings.setBackgroundColor( color );
1797 
1798  // background of the QGraphicsView
1799  QBrush bgBrush( color );
1800  setBackgroundBrush( bgBrush );
1801 #if 0
1802  QPalette palette;
1803  palette.setColor( backgroundRole(), color );
1804  setPalette( palette );
1805 #endif
1806 
1807  // background of QGraphicsScene
1808  mScene->setBackgroundBrush( bgBrush );
1809 
1810  refresh();
1811 
1812  emit canvasColorChanged();
1813 }
1814 
1816 {
1817  return mScene->backgroundBrush().color();
1818 }
1819 
1820 void QgsMapCanvas::setSelectionColor( const QColor &color )
1821 {
1822  if ( mSettings.selectionColor() == color )
1823  return;
1824 
1825  mSettings.setSelectionColor( color );
1826 
1827  if ( mCache )
1828  {
1829  bool hasSelectedFeatures = false;
1830  const auto layers = mSettings.layers();
1831  for ( QgsMapLayer *layer : layers )
1832  {
1833  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1834  if ( vlayer && vlayer->selectedFeatureCount() )
1835  {
1836  hasSelectedFeatures = true;
1837  break;
1838  }
1839  }
1840 
1841  if ( hasSelectedFeatures )
1842  {
1843  mCache->clear();
1844  refresh();
1845  }
1846  }
1847 }
1848 
1850 {
1851  return mSettings.selectionColor();
1852 }
1853 
1855 {
1856  return mapSettings().layers().size();
1857 } // layerCount
1858 
1859 
1860 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1861 {
1862  return mapSettings().layers();
1863 }
1864 
1866 {
1867  // called when a layer has changed visibility setting
1868  refresh();
1869 }
1870 
1871 void QgsMapCanvas::freeze( bool frozen )
1872 {
1873  mFrozen = frozen;
1874 }
1875 
1877 {
1878  return mFrozen;
1879 }
1880 
1882 {
1883  return mapSettings().mapUnitsPerPixel();
1884 }
1885 
1887 {
1888  return mapSettings().mapUnits();
1889 }
1890 
1891 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1892 {
1893  return mSettings.layerStyleOverrides();
1894 }
1895 
1896 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1897 {
1898  if ( overrides == mSettings.layerStyleOverrides() )
1899  return;
1900 
1901  mSettings.setLayerStyleOverrides( overrides );
1902  clearCache();
1904 }
1905 
1906 void QgsMapCanvas::setTheme( const QString &theme )
1907 {
1908  if ( mTheme == theme )
1909  return;
1910 
1911  clearCache();
1912  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1913  {
1914  mTheme.clear();
1915  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1916  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1917  emit themeChanged( QString() );
1918  }
1919  else
1920  {
1921  mTheme = theme;
1922  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1923  emit themeChanged( theme );
1924  }
1925 }
1926 
1928 {
1929  mRenderFlag = flag;
1930 
1931  if ( mRenderFlag )
1932  {
1933  refresh();
1934  }
1935  else
1936  stopRendering();
1937 }
1938 
1939 #if 0
1940 void QgsMapCanvas::connectNotify( const char *signal )
1941 {
1942  Q_UNUSED( signal )
1943  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1944 } //connectNotify
1945 #endif
1946 
1947 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1948 {
1949  if ( !deferred )
1950  refresh();
1951 }
1952 
1953 void QgsMapCanvas::autoRefreshTriggered()
1954 {
1955  if ( mJob )
1956  {
1957  // canvas is currently being redrawn, so we skip this auto refresh
1958  // otherwise we could get stuck in the situation where an auto refresh is triggered
1959  // too often to allow the canvas to ever finish rendering
1960  return;
1961  }
1962 
1963  refresh();
1964 }
1965 
1966 void QgsMapCanvas::updateAutoRefreshTimer()
1967 {
1968  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1969  // trigger a map refresh on this minimum interval
1970  int minAutoRefreshInterval = -1;
1971  const auto layers = mSettings.layers();
1972  for ( QgsMapLayer *layer : layers )
1973  {
1974  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1975  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1976  }
1977 
1978  if ( minAutoRefreshInterval > 0 )
1979  {
1980  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1981  mAutoRefreshTimer.start();
1982  }
1983  else
1984  {
1985  mAutoRefreshTimer.stop();
1986  }
1987 }
1988 
1989 void QgsMapCanvas::projectThemesChanged()
1990 {
1991  if ( mTheme.isEmpty() )
1992  return;
1993 
1994  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1995  {
1996  // theme has been removed - stop following
1997  setTheme( QString() );
1998  }
1999 
2000 }
2001 
2003 {
2004  return mMapTool;
2005 }
2006 
2007 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
2008 {
2009  // move map image and other items to standard position
2010  moveCanvasContents( true ); // true means reset
2011 
2012  // use start and end box points to calculate the extent
2013  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
2014  QgsPointXY end = getCoordinateTransform()->toMapCoordinates( releasePoint );
2015 
2016  // modify the center
2017  double dx = end.x() - start.x();
2018  double dy = end.y() - start.y();
2019  QgsPointXY c = center();
2020  c.set( c.x() - dx, c.y() - dy );
2021  setCenter( c );
2022 
2023  refresh();
2024 }
2025 
2026 void QgsMapCanvas::panAction( QMouseEvent *e )
2027 {
2028  Q_UNUSED( e )
2029 
2030  // move all map canvas items
2032 }
2033 
2035 {
2036  QPoint pnt( 0, 0 );
2037  if ( !reset )
2038  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
2039 
2040  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
2041 }
2042 
2043 void QgsMapCanvas::dropEvent( QDropEvent *event )
2044 {
2045  if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
2046  {
2047  const QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( event->mimeData() );
2048  bool allHandled = true;
2049  for ( const QgsMimeDataUtils::Uri &uri : lst )
2050  {
2051  bool handled = false;
2052  for ( QgsCustomDropHandler *handler : qgis::as_const( mDropHandlers ) )
2053  {
2054  if ( handler && handler->customUriProviderKey() == uri.providerKey )
2055  {
2056  if ( handler->handleCustomUriCanvasDrop( uri, this ) )
2057  {
2058  handled = true;
2059  break;
2060  }
2061  }
2062  }
2063  if ( !handled )
2064  allHandled = false;
2065  }
2066  if ( allHandled )
2067  event->accept();
2068  else
2069  event->ignore();
2070  }
2071  else
2072  {
2073  event->ignore();
2074  }
2075 }
2076 
2078 {
2079  return mCanvasProperties->mouseLastXY;
2080 }
2081 
2082 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
2083 {
2084  if ( !mPreviewEffect )
2085  {
2086  return;
2087  }
2088 
2089  mPreviewEffect->setEnabled( previewEnabled );
2090 }
2091 
2093 {
2094  if ( !mPreviewEffect )
2095  {
2096  return false;
2097  }
2098 
2099  return mPreviewEffect->isEnabled();
2100 }
2101 
2103 {
2104  if ( !mPreviewEffect )
2105  {
2106  return;
2107  }
2108 
2109  mPreviewEffect->setMode( mode );
2110 }
2111 
2113 {
2114  if ( !mPreviewEffect )
2115  {
2117  }
2118 
2119  return mPreviewEffect->mode();
2120 }
2121 
2123 {
2124  if ( !mSnappingUtils )
2125  {
2126  // associate a dummy instance, but better than null pointer
2127  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
2128  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
2129  }
2130  return mSnappingUtils;
2131 }
2132 
2134 {
2135  mSnappingUtils = utils;
2136 }
2137 
2138 void QgsMapCanvas::readProject( const QDomDocument &doc )
2139 {
2140  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
2141  if ( nodes.count() )
2142  {
2143  QDomNode node = nodes.item( 0 );
2144 
2145  // Search the specific MapCanvas node using the name
2146  if ( nodes.count() > 1 )
2147  {
2148  for ( int i = 0; i < nodes.size(); ++i )
2149  {
2150  QDomElement elementNode = nodes.at( i ).toElement();
2151 
2152  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
2153  {
2154  node = nodes.at( i );
2155  break;
2156  }
2157  }
2158  }
2159 
2160  QgsMapSettings tmpSettings;
2161  tmpSettings.readXml( node );
2162  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
2163  {
2164  // never manually set the crs for the main canvas - this is instead connected to the project CRS
2165  setDestinationCrs( tmpSettings.destinationCrs() );
2166  }
2167  setExtent( tmpSettings.extent() );
2168  setRotation( tmpSettings.rotation() );
2170 
2171  clearExtentHistory(); // clear the extent history on project load
2172 
2173  QDomElement elem = node.toElement();
2174  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
2175  {
2176  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
2177  {
2178  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
2179  }
2180  }
2181  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
2182 
2183  // restore canvas expression context
2184  const QDomNodeList scopeElements = elem.elementsByTagName( QStringLiteral( "expressionContextScope" ) );
2185  if ( scopeElements.size() > 0 )
2186  {
2187  const QDomElement scopeElement = scopeElements.at( 0 ).toElement();
2188  mExpressionContextScope.readXml( scopeElement, QgsReadWriteContext() );
2189  }
2190  }
2191  else
2192  {
2193  QgsDebugMsg( QStringLiteral( "Couldn't read mapcanvas information from project" ) );
2194  }
2195 }
2196 
2197 void QgsMapCanvas::writeProject( QDomDocument &doc )
2198 {
2199  // create node "mapcanvas" and call mMapRenderer->writeXml()
2200 
2201  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
2202  if ( !nl.count() )
2203  {
2204  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
2205  return;
2206  }
2207  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
2208 
2209  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
2210  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
2211  if ( !mTheme.isEmpty() )
2212  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
2213  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
2214  qgisNode.appendChild( mapcanvasNode );
2215 
2216  mSettings.writeXml( mapcanvasNode, doc );
2217 
2218  // store canvas expression context
2219  QDomElement scopeElement = doc.createElement( QStringLiteral( "expressionContextScope" ) );
2220  QgsExpressionContextScope tmpScope( mExpressionContextScope );
2221  tmpScope.removeVariable( QStringLiteral( "atlas_featurenumber" ) );
2222  tmpScope.removeVariable( QStringLiteral( "atlas_pagename" ) );
2223  tmpScope.removeVariable( QStringLiteral( "atlas_feature" ) );
2224  tmpScope.removeVariable( QStringLiteral( "atlas_featureid" ) );
2225  tmpScope.removeVariable( QStringLiteral( "atlas_geometry" ) );
2226  tmpScope.writeXml( scopeElement, doc, QgsReadWriteContext() );
2227  mapcanvasNode.appendChild( scopeElement );
2228 
2229  // TODO: store only units, extent, projections, dest CRS
2230 }
2231 
2232 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center )
2233 {
2234  if ( mScaleLocked )
2235  {
2236  // zoom map to mouse cursor by magnifying
2238  }
2239  else
2240  {
2242  r.scale( scaleFactor, center );
2243  setExtent( r, true );
2244  refresh();
2245  }
2246 }
2247 
2249 {
2250  // Find out which layer it was that sent the signal.
2251  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( sender() );
2252  if ( layer )
2253  {
2254  emit selectionChanged( layer );
2255  refresh();
2256  }
2257 }
2258 
2259 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *event )
2260 {
2261  // By default graphics view delegates the drag events to graphics items.
2262  // But we do not want that and by ignoring the drag enter we let the
2263  // parent (e.g. QgisApp) to handle drops of map layers etc.
2264 
2265  // so we ONLY accept the event if we know in advance that a custom drop handler
2266  // wants it
2267 
2268  if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
2269  {
2270  const QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( event->mimeData() );
2271  bool allHandled = true;
2272  for ( const QgsMimeDataUtils::Uri &uri : lst )
2273  {
2274  bool handled = false;
2275  for ( QgsCustomDropHandler *handler : qgis::as_const( mDropHandlers ) )
2276  {
2277  if ( handler->canHandleCustomUriCanvasDrop( uri, this ) )
2278  {
2279  handled = true;
2280  break;
2281  }
2282  }
2283  if ( !handled )
2284  allHandled = false;
2285  }
2286  if ( allHandled )
2287  event->accept();
2288  else
2289  event->ignore();
2290  }
2291  else
2292  {
2293  event->ignore();
2294  }
2295 }
2296 
2297 void QgsMapCanvas::mapToolDestroyed()
2298 {
2299  QgsDebugMsgLevel( QStringLiteral( "maptool destroyed" ), 2 );
2300  mMapTool = nullptr;
2301 }
2302 
2303 bool QgsMapCanvas::event( QEvent *e )
2304 {
2305  if ( !QTouchDevice::devices().empty() )
2306  {
2307  if ( e->type() == QEvent::Gesture )
2308  {
2309  // call handler of current map tool
2310  if ( mMapTool )
2311  {
2312  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2313  }
2314  }
2315  }
2316 
2317  // pass other events to base class
2318  return QGraphicsView::event( e );
2319 }
2320 
2322 {
2323  // reload all layers in canvas
2324  const QList<QgsMapLayer *> layers = mapSettings().layers();
2325  for ( QgsMapLayer *layer : layers )
2326  {
2327  layer->reload();
2328  }
2329 
2330  redrawAllLayers();
2331 }
2332 
2334 {
2335  // clear the cache
2336  clearCache();
2337 
2338  // and then refresh
2339  refresh();
2340 }
2341 
2343 {
2344  while ( mRefreshScheduled || mJob )
2345  {
2346  QgsApplication::processEvents();
2347  }
2348 }
2349 
2351 {
2352  mSettings.setSegmentationTolerance( tolerance );
2353 }
2354 
2356 {
2357  mSettings.setSegmentationToleranceType( type );
2358 }
2359 
2360 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2361 {
2362  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2363  const QList<QGraphicsItem *> items = mScene->items();
2364  for ( QGraphicsItem *gi : items )
2365  {
2366  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( gi );
2367  if ( aItem )
2368  {
2369  annotationItemList.push_back( aItem );
2370  }
2371  }
2372 
2373  return annotationItemList;
2374 }
2375 
2377 {
2378  mAnnotationsVisible = show;
2379  const QList<QgsMapCanvasAnnotationItem *> items = annotationItems();
2380  for ( QgsMapCanvasAnnotationItem *item : items )
2381  {
2382  item->setVisible( show );
2383  }
2384 }
2385 
2387 {
2388  mSettings.setLabelingEngineSettings( settings );
2389 }
2390 
2392 {
2393  return mSettings.labelingEngineSettings();
2394 }
2395 
2396 void QgsMapCanvas::startPreviewJobs()
2397 {
2398  stopPreviewJobs(); //just in case still running
2399  schedulePreviewJob( 0 );
2400 }
2401 
2402 void QgsMapCanvas::startPreviewJob( int number )
2403 {
2404  QgsRectangle mapRect = mSettings.visibleExtent();
2405 
2406  if ( number == 4 )
2407  number += 1;
2408 
2409  int j = number / 3;
2410  int i = number % 3;
2411 
2412  //copy settings, only update extent
2413  QgsMapSettings jobSettings = mSettings;
2414 
2415  double dx = ( i - 1 ) * mapRect.width();
2416  double dy = ( 1 - j ) * mapRect.height();
2417  QgsRectangle jobExtent = mapRect;
2418 
2419  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
2420  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
2421  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
2422  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
2423 
2424  jobSettings.setExtent( jobExtent );
2425  jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
2426  jobSettings.setFlag( QgsMapSettings::RenderPreviewJob, true );
2427 
2428  // truncate preview layers to fast layers
2429  const QList<QgsMapLayer *> layers = jobSettings.layers();
2430  QList< QgsMapLayer * > previewLayers;
2432  context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
2433  for ( QgsMapLayer *layer : layers )
2434  {
2435  context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
2436  if ( !layer->dataProvider()->renderInPreview( context ) )
2437  {
2438  QgsDebugMsgLevel( QStringLiteral( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
2439  continue;
2440  }
2441 
2442  previewLayers << layer;
2443  }
2444  jobSettings.setLayers( previewLayers );
2445 
2446  QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
2447  job->setProperty( "number", number );
2448  mPreviewJobs.append( job );
2449  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2450  job->start();
2451 }
2452 
2453 void QgsMapCanvas::stopPreviewJobs()
2454 {
2455  mPreviewTimer.stop();
2456  const auto previewJobs = mPreviewJobs;
2457  for ( auto previewJob : previewJobs )
2458  {
2459  if ( previewJob )
2460  {
2461  disconnect( previewJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
2462  connect( previewJob, &QgsMapRendererQImageJob::finished, previewJob, &QgsMapRendererQImageJob::deleteLater );
2463  previewJob->cancelWithoutBlocking();
2464  }
2465  }
2466  mPreviewJobs.clear();
2467 }
2468 
2469 void QgsMapCanvas::schedulePreviewJob( int number )
2470 {
2471  mPreviewTimer.setSingleShot( true );
2472  mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
2473  disconnect( mPreviewTimerConnection );
2474  mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
2475  {
2476  startPreviewJob( number );
2477  } );
2478  mPreviewTimer.start();
2479 }
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...
bool setReferencedExtent(const QgsReferencedRectangle &extent) SIP_THROW(QgsCsException)
Sets the canvas to the specified extent.
The class is used as a container of context for various read/write operations on other objects...
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
Wrapper for iterator of features from vector data provider or vector layer.
void updateCanvasItemPositions()
called on resize or changed extent to notify canvas items to change their rectangle ...
int autoRefreshInterval
Definition: qgsmaplayer.h:84
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:124
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
QPoint mouseLastXY
Last seen point of the mouse.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns global labeling engine settings from the internal map settings.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:79
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:904
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:157
void setRenderFlag(bool flag)
Sets whether a user has disabled canvas renders via the GUI.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds) ...
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
void zoomToNextExtent()
Zoom to the next extent (view)
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void zoomWithCenter(int x, int y, bool zoomIn)
Zooms in/out with a given center.
void setCanvasColor(const QColor &_newVal)
Write property of QColor bgColor.
void setCenter(const QgsPointXY &center)
Set the center of the map canvas, in geographical coordinates.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:151
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
QList< QgsMapLayer * > layers() const
Returns the list of layers shown within the map canvas.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads scope variables from an XML element.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
double magnificationFactor() const
Returns the magnification factor.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
void clearExtentHistory()
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
Maximum angle between generating radii (lines from arc center to output vertices) ...
bool event(QEvent *e) override
QColor selectionColor() const
Returns color for selected features.
bool mouseButtonDown
Flag to indicate status of mouse button.
void wheelEvent(QWheelEvent *e) override
void canvasColorChanged()
Emitted when canvas background color changes.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void stopRendering()
stop rendering (if there is any right now)
static UriList decodeUriList(const QMimeData *data)
double y
Definition: qgspointxy.h:48
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setPreviewJobsEnabled(bool enabled)
Sets whether canvas map preview jobs (low priority render jobs which render portions of the view just...
static QgsImageCache * imageCache()
Returns the application&#39;s image cache, used for caching resampled versions of raster images...
A class to represent a 2D point.
Definition: qgspointxy.h:43
double rotation() const
Gets the current map canvas rotation in clockwise degrees.
QgsPreviewEffect::PreviewMode previewMode() const
Returns the current preview mode for the map canvas.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:235
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
void dropEvent(QDropEvent *event) override
QColor backgroundColor() const
Gets the background color of the map.
void keyPressEvent(QKeyEvent *e) override
void zoomToFeatureExtent(QgsRectangle &rect)
Zooms to feature extent.
Allow zooming by rectangle (by holding shift and dragging) while the tool is active.
Definition: qgsmaptool.h:94
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
void enableAntiAliasing(bool flag)
used to determine if anti-aliasing is enabled or not
An abstract class for items that can be placed on the map canvas.
void setCurrentLayer(QgsMapLayer *layer)
int layerCount() const
Returns number of layers on the map.
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
void moveCanvasContents(bool reset=false)
called when panning is in action, reset indicates end of panning
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setFlags(QgsMapSettings::Flags flags)
Sets combination of flags that will be used for rendering.
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
constexpr double CANVAS_MAGNIFICATION_MAX
Maximum magnification level allowed in map canvases.
Definition: qgsguiutils.h:69
void readProject(const QDomDocument &)
called to read map canvas settings from project
bool panSelectorDown
Flag to indicate the pan selector key is held down by user.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
void renderComplete(QPainter *)
Emitted when the canvas has rendered.
void projectColorsChanged()
Emitted whenever the project&#39;s color scheme has been changed.
Snapping utils instance that is connected to a canvas and updates the configuration (map settings + c...
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QgsMapTool * mapTool()
Returns the currently active tool.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
void mousePressEvent(QMouseEvent *e) override
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:102
void mapThemesChanged()
Emitted when map themes within the collection are changed.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
virtual QImage renderedImage()=0
Gets a preview/resulting image.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:75
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
QgsExpressionContextScope * defaultExpressionContextScope()
Creates a new scope which contains default variables and functions relating to the map canvas...
void selectionChanged(QgsVectorLayer *layer)
Emitted when selection in any layer gets changed.
Abstract base class that may be implemented to handle new types of data to be dropped in QGIS...
Enable drawing of labels on top of the map.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
double maxRenderingTimeMs
Default maximum allowable render time, in ms.
void zoomLastStatusChanged(bool)
Emitted when zoom last status changed.
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
virtual void canvasPressEvent(QgsMapMouseEvent *e)
Mouse press event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:167
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
void magnificationChanged(double)
Emitted when the scale of the map changes.
QString what() const
Definition: qgsexception.h:48
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void updateScale()
Emits signal scaleChanged to update scale in main window.
QgsUnitTypes::DistanceUnit mapUnits() const
Gets units of map&#39;s geographical coordinates - used for scale calculation.
static bool isUriList(const QMimeData *data)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
void resizeEvent(QResizeEvent *e) override
Deprecated to be deleted, stuff from here should be moved elsewhere.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:175
void enableMapTileRendering(bool flag)
sets map tile rendering flag
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:83
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
void setSnappingUtils(QgsSnappingUtils *utils)
Assign an instance of snapping utils to the map canvas.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
virtual void keyReleaseEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:187
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
QgsRectangle boundingBoxOfSelected() const
Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,0,0,0) is returned.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
Map tool is an edit tool, which can only be used when layer is editable.
Definition: qgsmaptool.h:93
void setOutputSize(QSize size)
Sets the size of the resulting map image.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
void start() override
Start the rendering job and immediately return.
void saveAsImage(const QString &fileName, QPixmap *QPixmap=nullptr, const QString &="PNG")
Save the contents of the map canvas to disk as an image.
QgsMapThemeCollection mapThemeCollection
Definition: qgsproject.h:98
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global labeling engine settings in the internal map settings.
void mapCanvasRefreshed()
Emitted when canvas finished a refresh request.
void rotationChanged(double)
Emitted when the rotation of the map changes.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setMagnificationFactor(double factor)
Set the magnification factor.
void zoomNextStatusChanged(bool)
Emitted when zoom next status changed.
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
A circle is used to highlight points (â—‹)
Definition: qgsrubberband.h:95
void flashFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of features with matching ids from a vector layer to flash within the canvas...
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled) ...
void reload() FINAL
Synchronises with changes in the datasource.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsMapCanvas(QWidget *parent=nullptr)
Constructor.
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:48
double scale() const
Returns the calculated map scale.
Job implementation that renders all layers in parallel.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
void setMapUpdateInterval(int timeMilliseconds)
Set how often map preview should be updated while it is being rendered (in milliseconds) ...
void keyReleased(QKeyEvent *e)
Emit key release event.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
virtual void waitForFinished()=0
Block until the job has finished.
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
void setDevicePixelRatio(float dpr)
Sets the device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" d...
void readProject(const QDomDocument &)
Emitted when a project is being read.
Enable anti-aliasing for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:812
void mouseDoubleClickEvent(QMouseEvent *e) override
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.
void setCustomDropHandlers(const QVector< QPointer< QgsCustomDropHandler >> &handlers)
Sets a list of custom drop handlers to use when drop events occur on the canvas.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
void setWheelFactor(double factor)
Sets wheel zoom factor (should be greater than 1)
void destinationCrsChanged()
Emitted when map CRS has changed.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
bool isFrozen() const
Returns true if canvas is frozen.
A QgsRectangle with associated coordinate reference system.
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:99
Single scope for storing variables and functions for use within a QgsExpressionContext.
double mapUnitsPerPixel() const
Returns current map units per pixel.
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets a preview mode for the map canvas.
virtual void wheelEvent(QWheelEvent *e)
Mouse wheel event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:177
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
void transformContextChanged()
Emitted when the canvas transform context is changed.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes scope variables to an XML element.
void setMode(PreviewMode mode)
Sets the mode for the preview effect, which controls how the effect modifies a widgets appearance...
void renderStarting()
Emitted when the canvas is about to be rendered.
float devicePixelRatio() const
Returns device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" di...
const QgsMapToPixel & mapToPixel() const
void keyPressed(QKeyEvent *e)
Emit key press event.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
void zoomOut()
Zoom out with fixed factor.
Enable drawing of vertex markers for layers in editing mode.
void waitWhileRendering()
Blocks until the rendering job has finished.
constexpr double CANVAS_MAGNIFICATION_MIN
Minimum magnification level allowed in map canvases.
Definition: qgsguiutils.h:61
void zoomToPreviousExtent()
Zoom to the previous extent (view)
bool isDrawing()
Find out whether rendering is in progress.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
double x
Definition: qgspointxy.h:47
virtual bool renderInPreview(const QgsDataProvider::PreviewContext &context)
Returns whether the layer must be rendered in preview jobs.
void zoomToSelected(QgsVectorLayer *layer=nullptr)
Zoom to the extent of the selected features of provided (vector) layer.
A class to represent a vector.
Definition: qgsvector.h:29
PreviewMode mode() const
Returns the mode used for the preview effect.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
virtual void start()=0
Start the rendering job and immediately return.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QPoint mouseLastXY()
returns last position of mouse cursor
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
double magnificationFactor() const
Returns the magnification factor.
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QMap< QString, QString > layerStyleOverrides() const
Returns the stored overrides of styles for layers.
void mouseMoveEvent(QMouseEvent *e) override
void keyReleaseEvent(QKeyEvent *e) override
virtual void keyPressEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:182
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:66
void flashGeometries(const QList< QgsGeometry > &geometries, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of geometries to flash within the canvas.
double scale() const
Returns the last reported scale of the canvas.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
QColor canvasColor() const
Read property of QColor bgColor.
void clear()
Invalidates the cache contents, clearing all cached images.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
Abstract base class for all map tools.
Definition: qgsmaptool.h:62
void selectionChangedSlot()
Receives signal about selection change, and pass it on with layer info.
Draw map such that there are no problems between adjacent tiles.
Job implementation that renders everything sequentially in one thread.
Render is a &#39;canvas preview&#39; render, and shortcuts should be taken to ensure fast rendering...
QgsUnitTypes::DistanceUnit mapUnits() const
Convenience function for returning the current canvas map units.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void mouseReleaseEvent(QMouseEvent *e) override
void writeProject(QDomDocument &)
Emitted when the project is being written.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:227
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QString theme() const
Returns the map&#39;s theme shown in the canvas, if set.
Definition: qgsmapcanvas.h:437
QgsPointXY center() const
Gets map center, in geographical coordinates.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
virtual bool gestureEvent(QGestureEvent *e)
gesture event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:192
void setSelectionColor(const QColor &color)
Sets color that is used for drawing of selected vector features.
void layerStyleOverridesChanged()
Emitted when the configuration of overridden layer styles changes.
virtual void canvasDoubleClickEvent(QgsMapMouseEvent *e)
Mouse double-click event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:162
void dragEnterEvent(QDragEnterEvent *e) override
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer&#39;s CRS to output CRS
void writeProject(QDomDocument &)
called to write map canvas settings to project
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
void setLayers(const QList< QgsMapLayer *> &layers)
Sets the list of layers that should be shown in the canvas.
QgsRectangle fullExtent() const
returns current extent of layer set
Intermediate base class adding functionality that allows client to query the rendered image...
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter=true)
Centers canvas extent to feature ids.
Stores global configuration for labeling engine.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:442
void zoomToFullExtent()
Zoom to the full extent of all layers.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
This class has all the configuration of snapping and can return answers to snapping queries...
const QgsLabelingResults * labelingResults() const
Gets access to the labeling results (may be nullptr)
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
void setMapSettingsFlags(QgsMapSettings::Flags flags)
Resets the flags for the canvas&#39; map settings.
void refreshAllLayers()
Reload all layers (including refreshing layer properties from their data sources), clears the cache and refreshes the canvas.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
QList< QgsMimeDataUtils::Uri > UriList
void zoomScale(double scale)
Zooms the canvas to a specific scale.
Class for doing transforms between two map coordinate systems.
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
void renderErrorOccurred(const QString &error, QgsMapLayer *layer)
Emitted whenever an error is encountered during a map render operation.
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
Definition: qgssettings.h:244
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
virtual void cancelWithoutBlocking()=0
Triggers cancellation of the rendering job without blocking.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
void setSelectionColor(const QColor &color)
Set color of selected vector features.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
void paintEvent(QPaintEvent *e) override
void layerStateChange()
This slot is connected to the visibility change of one or more layers.
double lastRenderingTimeMs
Previous rendering time for the layer, in ms.
Enable vector simplification and other rendering optimizations.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
void freeze(bool frozen=true)
Freeze/thaw the map canvas.
void setScaleLocked(bool isLocked)
Lock the scale, so zooming can be performed using magnication.
Class that stores computed placement from labeling engine.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
This class is responsible for keeping cache of rendered images resulting from a map rendering job...
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QgsGeometry geometry
Definition: qgsfeature.h:67
void transformContextChanged()
Emitted when the project transformContext() is changed.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
QColor selectionColor() const
Gets color that is used for drawing of selected vector features.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
void readXml(QDomNode &node)
bool nextFeature(QgsFeature &f)
QPoint rubberStartPoint
Beginning point of a rubber band.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
virtual QgsLabelingResults * takeLabelingResults()=0
Gets pointer to internal labeling engine (in order to get access to the results). ...
void xyCoordinates(const QgsPointXY &p)
Emits current mouse position.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void writeXml(QDomNode &node, QDomDocument &doc)
void zoomIn()
Zoom in with fixed factor.
Stores settings related to the context in which a preview job runs.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
virtual void updatePosition()
called on changed extent or resize event to update position of the item
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
CanvasProperties()=default
Constructor for CanvasProperties.
An interactive map canvas item which displays a QgsAnnotation.
void themeChanged(const QString &theme)
Emitted when the canvas has been assigned a different map theme.
void extentsChanged()
Emitted when the extents of the map change.
virtual void clean()
convenient method to clean members
Definition: qgsmaptool.cpp:109
QSize outputSize() const
Returns the size of the resulting map image.
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
QgsMapLayer * layer(int index)
Returns the map layer at position index in the layer stack.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
virtual bool isActive() const =0
Tell whether the rendering job is currently running in background.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
void setMagnificationFactor(double factor)
Sets the factor of magnification to apply to the map canvas.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
void panActionEnd(QPoint releasePoint)
Ends pan action and redraws the canvas.
void redrawAllLayers()
Clears all cached images and redraws all layers.
bool previewModeEnabled() const
Returns whether a preview mode is enabled for the map canvas.
void layersChanged()
Emitted when a new set of layers has been received.
void messageEmitted(const QString &title, const QString &message, Qgis::MessageLevel=Qgis::Info)
emit a message (usually to be displayed in a message bar)
virtual bool usedCachedLabels() const =0
Returns true if the render job was able to use a cached labeling solution.
~QgsMapCanvas() override
virtual void canvasReleaseEvent(QgsMapMouseEvent *e)
Mouse release event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:172