QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 #include <QMenu>
41 #include <QClipboard>
42 #include <QVariantAnimation>
43 #include <QPropertyAnimation>
44 
45 #include "qgis.h"
46 #include "qgssettings.h"
48 #include "qgsapplication.h"
49 #include "qgsexception.h"
51 #include "qgsfeatureiterator.h"
52 #include "qgslogger.h"
53 #include "qgsmapcanvas.h"
54 #include "qgsmapcanvasmap.h"
56 #include "qgsmaplayer.h"
57 #include "qgsmapmouseevent.h"
58 #include "qgsmaptoolpan.h"
59 #include "qgsmaptoolzoom.h"
60 #include "qgsmaptopixel.h"
61 #include "qgsmapoverviewcanvas.h"
62 #include "qgsmaprenderercache.h"
64 #include "qgsmaprendererjob.h"
67 #include "qgsmapsettingsutils.h"
68 #include "qgsmessagelog.h"
69 #include "qgsmessageviewer.h"
70 #include "qgspallabeling.h"
71 #include "qgsproject.h"
72 #include "qgsrubberband.h"
73 #include "qgsvectorlayer.h"
74 #include "qgsmapthemecollection.h"
76 #include "qgssvgcache.h"
77 #include "qgsimagecache.h"
79 #include "qgsmimedatautils.h"
80 #include "qgscustomdrophandler.h"
81 #include "qgsreferencedgeometry.h"
82 #include "qgsprojectviewsettings.h"
86 #include "qgstemporalcontroller.h"
87 #include "qgsruntimeprofiler.h"
89 #include "qgsannotationlayer.h"
92 #include "qgslabelingresults.h"
93 #include "qgsmaplayerutils.h"
94 #include "qgssettingsregistrygui.h"
95 #include "qgsrendereditemresults.h"
96 
102 //TODO QGIS 4.0 - remove
104 {
105  public:
106 
110  CanvasProperties() = default;
111 
113  bool mouseButtonDown{ false };
114 
116  QPoint mouseLastXY;
117 
120 
122  bool panSelectorDown{ false };
123 };
124 
125 
126 
127 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
128  : QGraphicsView( parent )
129  , mCanvasProperties( new CanvasProperties )
130  , mExpressionContextScope( tr( "Map Canvas" ) )
131 {
132  mScene = new QGraphicsScene();
133  setScene( mScene );
134  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
135  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
136  setMouseTracking( true );
137  setFocusPolicy( Qt::StrongFocus );
138 
139  mResizeTimer = new QTimer( this );
140  mResizeTimer->setSingleShot( true );
141  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
142 
143  mRefreshTimer = new QTimer( this );
144  mRefreshTimer->setSingleShot( true );
145  connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
146 
147  // create map canvas item which will show the map
148  mMap = new QgsMapCanvasMap( this );
149 
150  // project handling
152  this, &QgsMapCanvas::readProject );
155 
156  connect( QgsProject::instance()->mainAnnotationLayer(), &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
157  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
158  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeRenamed, this, &QgsMapCanvas::mapThemeRenamed );
159  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
160 
161  {
162  QgsScopedRuntimeProfile profile( "Map settings initialization" );
166  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
168  this, [ = ]
169  {
170  mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
171  refresh();
172  } );
175  this, [ = ]
176  {
177  mSettings.setTransformContext( QgsProject::instance()->transformContext() );
179  refresh();
180  } );
181 
183  {
186  if ( mSettings.destinationCrs() != crs )
187  {
188  // user crs has changed definition, refresh the map
189  setDestinationCrs( crs );
190  }
191  } );
192  }
193 
194  // refresh canvas when a remote svg/image has finished downloading
197  // refresh canvas when project color scheme is changed -- if layers use project colors, they need to be redrawn
199 
200  //segmentation parameters
201  QgsSettings settings;
202  double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
203  QgsAbstractGeometry::SegmentationToleranceType toleranceType = settings.enumValue( QStringLiteral( "qgis/segmentationToleranceType" ), QgsAbstractGeometry::MaximumAngle );
204  mSettings.setSegmentationTolerance( segmentationTolerance );
205  mSettings.setSegmentationToleranceType( toleranceType );
206 
207  mWheelZoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
208 
209  QSize s = viewport()->size();
210  mSettings.setOutputSize( s );
211 
213 
214  setSceneRect( 0, 0, s.width(), s.height() );
215  mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
216 
217  moveCanvasContents( true );
218 
219  connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsMapCanvas::mapUpdateTimeout );
220  mMapUpdateTimer.setInterval( 250 );
221 
222 #ifdef Q_OS_WIN
223  // Enable touch event on Windows.
224  // Qt on Windows needs to be told it can take touch events or else it ignores them.
225  grabGesture( Qt::PinchGesture );
226  grabGesture( Qt::TapAndHoldGesture );
227  viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
228 #endif
229 
230  mPreviewEffect = new QgsPreviewEffect( this );
231  viewport()->setGraphicsEffect( mPreviewEffect );
232 
233  mZoomCursor = QgsApplication::getThemeCursor( QgsApplication::Cursor::ZoomIn );
234 
235  connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
236 
238 
239  setInteractive( false );
240 
241  // make sure we have the same default in QgsMapSettings and the scene's background brush
242  // (by default map settings has white bg color, scene background brush is black)
243  setCanvasColor( mSettings.backgroundColor() );
244 
245  setTemporalRange( mSettings.temporalRange() );
246  refresh();
247 }
248 
249 
251 {
252  if ( mMapTool )
253  {
254  mMapTool->deactivate();
255  mMapTool = nullptr;
256  }
257  mLastNonZoomMapTool = nullptr;
258 
259  cancelJobs();
260 
261  // delete canvas items prior to deleting the canvas
262  // because they might try to update canvas when it's
263  // already being destructed, ends with segfault
264  qDeleteAll( mScene->items() );
265 
266  mScene->deleteLater(); // crashes in python tests on windows
267 
268  delete mCache;
269 }
270 
271 
273 {
274 
275  // rendering job may still end up writing into canvas map item
276  // so kill it before deleting canvas items
277  if ( mJob )
278  {
279  whileBlocking( mJob )->cancel();
280  delete mJob;
281  mJob = nullptr;
282  }
283 
284  QList< QgsMapRendererQImageJob * >::const_iterator previewJob = mPreviewJobs.constBegin();
285  for ( ; previewJob != mPreviewJobs.constEnd(); ++previewJob )
286  {
287  if ( *previewJob )
288  {
289  whileBlocking( *previewJob )->cancel();
290  delete *previewJob;
291  }
292  }
293 }
294 
295 
296 void QgsMapCanvas::setMagnificationFactor( double factor, const QgsPointXY *center )
297 {
298  // do not go higher or lower than min max magnification ratio
299  double magnifierMin = QgsGuiUtils::CANVAS_MAGNIFICATION_MIN;
300  double magnifierMax = QgsGuiUtils::CANVAS_MAGNIFICATION_MAX;
301  factor = std::clamp( factor, magnifierMin, magnifierMax );
302 
303  // the magnifier widget is in integer percent
304  if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
305  {
306  mSettings.setMagnificationFactor( factor, center );
307  refresh();
308  emit magnificationChanged( factor );
309  }
310 }
311 
313 {
314  return mSettings.magnificationFactor();
315 }
316 
318 {
319  mSettings.setFlag( Qgis::MapSettingsFlag::Antialiasing, flag );
321 }
322 
324 {
325  return mSettings.testFlag( Qgis::MapSettingsFlag::Antialiasing );
326 }
327 
329 {
331 }
332 
334 {
335  QList<QgsMapLayer *> layers = mapSettings().layers();
336  if ( index >= 0 && index < layers.size() )
337  return layers[index];
338  else
339  return nullptr;
340 }
341 
342 QgsMapLayer *QgsMapCanvas::layer( const QString &id )
343 {
344  // first check for layers from canvas map settings
345  const QList<QgsMapLayer *> layers = mapSettings().layers();
346  for ( QgsMapLayer *layer : layers )
347  {
348  if ( layer && layer->id() == id )
349  return layer;
350  }
351 
352  // else fallback to searching project layers
353  // TODO: allow a specific project to be associated with a canvas!
354  return QgsProject::instance()->mapLayer( id );
355 }
356 
358 {
359  if ( mCurrentLayer == layer )
360  return;
361 
362  mCurrentLayer = layer;
363  emit currentLayerChanged( layer );
364 }
365 
366 double QgsMapCanvas::scale() const
367 {
368  return mapSettings().scale();
369 }
370 
372 {
373  return nullptr != mJob;
374 } // isDrawing
375 
376 // return the current coordinate transform based on the extents and
377 // device size
379 {
380  return &mapSettings().mapToPixel();
381 }
382 
383 void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
384 {
385  // following a theme => request denied!
386  if ( !mTheme.isEmpty() )
387  return;
388 
389  setLayersPrivate( layers );
390 }
391 
392 void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
393 {
394  QList<QgsMapLayer *> oldLayers = mSettings.layers();
395 
396  // update only if needed
397  if ( layers == oldLayers )
398  return;
399 
400  const auto constOldLayers = oldLayers;
401  for ( QgsMapLayer *layer : constOldLayers )
402  {
403  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
404  disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
405  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
406  {
408  }
409  }
410 
411  mSettings.setLayers( layers );
412 
413  const auto constLayers = layers;
414  for ( QgsMapLayer *layer : constLayers )
415  {
416  if ( !layer )
417  continue;
418  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
419  connect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
420  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
421  {
423  }
424  }
425 
426  QgsDebugMsgLevel( QStringLiteral( "Layers have changed, refreshing" ), 2 );
427  emit layersChanged();
428 
429  updateAutoRefreshTimer();
430  refresh();
431 }
432 
433 
435 {
436  return mSettings;
437 }
438 
440 {
441  if ( mSettings.destinationCrs() == crs )
442  return;
443 
444  // try to reproject current extent to the new one
445  QgsRectangle rect;
446  if ( !mSettings.visibleExtent().isEmpty() )
447  {
448  QgsCoordinateTransform transform( mSettings.destinationCrs(), crs, QgsProject::instance() );
449  transform.setBallparkTransformsAreAppropriate( true );
450  try
451  {
452  rect = transform.transformBoundingBox( mSettings.visibleExtent() );
453  }
454  catch ( QgsCsException &e )
455  {
456  Q_UNUSED( e )
457  QgsDebugMsg( QStringLiteral( "Transform error caught: %1" ).arg( e.what() ) );
458  }
459  }
460 
461  if ( !rect.isEmpty() )
462  {
463  // we will be manually calling updateCanvasItemPositions() later, AFTER setting the updating the mSettings destination CRS, and we don't
464  // want to do that twice!
465  mBlockItemPositionUpdates++;
466  setExtent( rect );
467  mBlockItemPositionUpdates--;
468  }
469 
470  mSettings.setDestinationCrs( crs );
471  updateScale();
473 
474  QgsDebugMsgLevel( QStringLiteral( "refreshing after destination CRS changed" ), 2 );
475  refresh();
476 
477  emit destinationCrsChanged();
478 }
479 
481 {
482  if ( mController )
484 
485  mController = controller;
487 }
488 
490 {
491  return mController;
492 }
493 
494 void QgsMapCanvas::setMapSettingsFlags( Qgis::MapSettingsFlags flags )
495 {
496  mSettings.setFlags( flags );
497  clearCache();
498  refresh();
499 }
500 
501 const QgsLabelingResults *QgsMapCanvas::labelingResults( bool allowOutdatedResults ) const
502 {
503  if ( !allowOutdatedResults && mLabelingResultsOutdated )
504  return nullptr;
505 
506  return mLabelingResults.get();
507 }
508 
509 const QgsRenderedItemResults *QgsMapCanvas::renderedItemResults( bool allowOutdatedResults ) const
510 {
511  if ( !allowOutdatedResults && mRenderedItemResultsOutdated )
512  return nullptr;
513 
514  return mRenderedItemResults.get();
515 }
516 
518 {
519  if ( enabled == isCachingEnabled() )
520  return;
521 
522  if ( mJob && mJob->isActive() )
523  {
524  // wait for the current rendering to finish, before touching the cache
525  mJob->waitForFinished();
526  }
527 
528  if ( enabled )
529  {
530  mCache = new QgsMapRendererCache;
531  }
532  else
533  {
534  delete mCache;
535  mCache = nullptr;
536  }
537  mPreviousRenderedItemResults.reset();
538 }
539 
541 {
542  return nullptr != mCache;
543 }
544 
546 {
547  if ( mCache )
548  mCache->clear();
549 
550  if ( mPreviousRenderedItemResults )
551  mPreviousRenderedItemResults.reset();
552  if ( mRenderedItemResults )
553  mRenderedItemResults.reset();
554 }
555 
557 {
558  mUseParallelRendering = enabled;
559 }
560 
562 {
563  return mUseParallelRendering;
564 }
565 
566 void QgsMapCanvas::setMapUpdateInterval( int timeMilliseconds )
567 {
568  mMapUpdateTimer.setInterval( timeMilliseconds );
569 }
570 
572 {
573  return mMapUpdateTimer.interval();
574 }
575 
576 
578 {
579  return mCurrentLayer;
580 }
581 
583 {
584  QgsExpressionContextScope *s = new QgsExpressionContextScope( QObject::tr( "Map Canvas" ) );
585  s->setVariable( QStringLiteral( "canvas_cursor_point" ), QgsGeometry::fromPointXY( cursorPoint() ), true );
586  return s;
587 }
588 
590 {
591  //build the expression context
592  QgsExpressionContext expressionContext;
593  expressionContext << QgsExpressionContextUtils::globalScope()
597  if ( QgsExpressionContextScopeGenerator *generator = dynamic_cast< QgsExpressionContextScopeGenerator * >( mController ) )
598  {
599  expressionContext << generator->createExpressionContextScope();
600  }
601  expressionContext << defaultExpressionContextScope()
602  << new QgsExpressionContextScope( mExpressionContextScope );
603  return expressionContext;
604 }
605 
607 {
608  if ( !mSettings.hasValidSettings() )
609  {
610  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh - invalid settings -> nothing to do" ), 2 );
611  return;
612  }
613 
614  if ( !mRenderFlag || mFrozen )
615  {
616  QgsDebugMsgLevel( QStringLiteral( "CANVAS render flag off" ), 2 );
617  return;
618  }
619 
620  if ( mRefreshScheduled )
621  {
622  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh already scheduled" ), 2 );
623  return;
624  }
625 
626  mRefreshScheduled = true;
627 
628  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh scheduling" ), 2 );
629 
630  // schedule a refresh
631  mRefreshTimer->start( 1 );
632 
633  mLabelingResultsOutdated = true;
634  mRenderedItemResultsOutdated = true;
635 }
636 
637 void QgsMapCanvas::refreshMap()
638 {
639  Q_ASSERT( mRefreshScheduled );
640 
641  QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh!" ), 3 );
642 
643  stopRendering(); // if any...
644  stopPreviewJobs();
645 
647  mSettings.setPathResolver( QgsProject::instance()->pathResolver() );
648 
649  if ( !mTheme.isEmpty() )
650  {
651  // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
652  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
653  // current state of the style. If we had stored the style overrides earlier (such as in
654  // mapThemeChanged slot) then this xml could be out of date...
655  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
656  // just return the style name, we can instead set the overrides in mapThemeChanged and not here
657  mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
658  }
659 
660  // render main annotation layer above all other layers
661  QgsMapSettings renderSettings = mSettings;
662  QList<QgsMapLayer *> allLayers = renderSettings.layers();
663  allLayers.insert( 0, QgsProject::instance()->mainAnnotationLayer() );
664  renderSettings.setLayers( allLayers );
665 
666  // create the renderer job
667  Q_ASSERT( !mJob );
668  mJobCanceled = false;
669  if ( mUseParallelRendering )
670  mJob = new QgsMapRendererParallelJob( renderSettings );
671  else
672  mJob = new QgsMapRendererSequentialJob( renderSettings );
673 
674  connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
675  mJob->setCache( mCache );
676  mJob->setLayerRenderingTimeHints( mLastLayerRenderTime );
677 
678  mJob->start();
679 
680  // from now on we can accept refresh requests again
681  // this must be reset only after the job has been started, because
682  // some providers (yes, it's you WCS and AMS!) during preparation
683  // do network requests and start an internal event loop, which may
684  // end up calling refresh() and would schedule another refresh,
685  // deleting the one we have just started.
686  mRefreshScheduled = false;
687 
688  mMapUpdateTimer.start();
689 
690  emit renderStarting();
691 }
692 
693 void QgsMapCanvas::mapThemeChanged( const QString &theme )
694 {
695  if ( theme == mTheme )
696  {
697  // set the canvas layers to match the new layers contained in the map theme
698  // NOTE: we do this when the theme layers change and not when we are refreshing the map
699  // as setLayers() sets up necessary connections to handle changes to the layers
700  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
701  // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
702  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
703  // current state of the style. If changes were made to the style then this xml
704  // snapshot goes out of sync...
705  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
706  // just return the style name, we can instead set the overrides here and not in refreshMap()
707 
708  clearCache();
709  refresh();
710  }
711 }
712 
713 void QgsMapCanvas::mapThemeRenamed( const QString &theme, const QString &newTheme )
714 {
715  if ( mTheme.isEmpty() || theme != mTheme )
716  {
717  return;
718  }
719 
720  setTheme( newTheme );
721  refresh();
722 }
723 
724 void QgsMapCanvas::rendererJobFinished()
725 {
726  QgsDebugMsgLevel( QStringLiteral( "CANVAS finish! %1" ).arg( !mJobCanceled ), 2 );
727 
728  mMapUpdateTimer.stop();
729 
730  notifyRendererErrors( mJob->errors() );
731 
732  if ( !mJobCanceled )
733  {
734  // take labeling results before emitting renderComplete, so labeling map tools
735  // connected to signal work with correct results
736  if ( !mJob->usedCachedLabels() )
737  {
738  mLabelingResults.reset( mJob->takeLabelingResults() );
739  }
740  mLabelingResultsOutdated = false;
741 
742  std::unique_ptr< QgsRenderedItemResults > renderedItemResults( mJob->takeRenderedItemResults() );
743  // if a layer was redrawn from the cached version, we should copy any existing rendered item results from that layer
744  if ( mRenderedItemResults )
745  {
746  renderedItemResults->transferResults( mRenderedItemResults.get(), mJob->layersRedrawnFromCache() );
747  }
748  if ( mPreviousRenderedItemResults )
749  {
750  // also transfer any results from previous renders which happened before this
751  renderedItemResults->transferResults( mPreviousRenderedItemResults.get(), mJob->layersRedrawnFromCache() );
752  }
753 
754  if ( mCache && !mPreviousRenderedItemResults )
755  mPreviousRenderedItemResults = std::make_unique< QgsRenderedItemResults >( mJob->mapSettings().extent() );
756 
757  if ( mRenderedItemResults && mPreviousRenderedItemResults )
758  {
759  // for other layers which ARE present in the most recent rendered item results BUT were not part of this render, we
760  // store the results in a temporary store in case they are later switched back on and the layer's image is taken
761  // from the cache
762  mPreviousRenderedItemResults->transferResults( mRenderedItemResults.get() );
763  }
764  if ( mPreviousRenderedItemResults )
765  {
766  mPreviousRenderedItemResults->eraseResultsFromLayers( mJob->mapSettings().layerIds() );
767  }
768 
769  mRenderedItemResults = std::move( renderedItemResults );
770  mRenderedItemResultsOutdated = false;
771 
772  QImage img = mJob->renderedImage();
773 
774  // emit renderComplete to get our decorations drawn
775  QPainter p( &img );
776  emit renderComplete( &p );
777 
779  {
780  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
781  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
782  }
783 
784  if ( mDrawRenderingStats )
785  {
786  int w = img.width(), h = img.height();
787  QFont fnt = p.font();
788  fnt.setBold( true );
789  p.setFont( fnt );
790  int lh = p.fontMetrics().height() * 2;
791  QRect r( 0, h - lh, w, lh );
792  p.setPen( Qt::NoPen );
793  p.setBrush( QColor( 0, 0, 0, 110 ) );
794  p.drawRect( r );
795  p.setPen( Qt::white );
796  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? QStringLiteral( "PARALLEL" ) : QStringLiteral( "SEQUENTIAL" ) ).arg( mJob->renderingTime() );
797  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
798  }
799 
800  p.end();
801 
802  mMap->setContent( img, imageRect( img, mSettings ) );
803 
804  mLastLayerRenderTime.clear();
805  const auto times = mJob->perLayerRenderingTime();
806  for ( auto it = times.constBegin(); it != times.constEnd(); ++it )
807  {
808  mLastLayerRenderTime.insert( it.key()->id(), it.value() );
809  }
810  if ( mUsePreviewJobs && !mRefreshAfterJob )
811  startPreviewJobs();
812  }
813  else
814  {
815  mRefreshAfterJob = false;
816  }
817 
818  // now we are in a slot called from mJob - do not delete it immediately
819  // so the class is still valid when the execution returns to the class
820  mJob->deleteLater();
821  mJob = nullptr;
822 
823  emit mapCanvasRefreshed();
824 
825  if ( mRefreshAfterJob )
826  {
827  mRefreshAfterJob = false;
828  clearTemporalCache();
829  clearElevationCache();
830  refresh();
831  }
832 }
833 
834 void QgsMapCanvas::previewJobFinished()
835 {
836  QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
837  Q_ASSERT( job );
838 
839  if ( mMap )
840  {
841  mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
842  mPreviewJobs.removeAll( job );
843 
844  int number = job->property( "number" ).toInt();
845  if ( number < 8 )
846  {
847  startPreviewJob( number + 1 );
848  }
849 
850  delete job;
851  }
852 }
853 
854 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
855 {
856  // This is a hack to pass QgsMapCanvasItem::setRect what it
857  // expects (encoding of position and size of the item)
858  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
859  QgsPointXY topLeft = m2p.toMapCoordinates( 0, 0 );
860 #ifdef QGISDEBUG
861  // do not assert this, since it might lead to crashes when changing screen while rendering
862  if ( img.devicePixelRatio() != mapSettings.devicePixelRatio() )
863  {
864  QgsLogger::warning( QStringLiteral( "The renderer map has a wrong device pixel ratio" ) );
865  }
866 #endif
867  double res = m2p.mapUnitsPerPixel() / img.devicePixelRatioF();
868  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
869  return rect;
870 }
871 
873 {
874  return mUsePreviewJobs;
875 }
876 
878 {
879  mUsePreviewJobs = enabled;
880 }
881 
882 void QgsMapCanvas::setCustomDropHandlers( const QVector<QPointer<QgsCustomDropHandler> > &handlers )
883 {
884  mDropHandlers = handlers;
885 }
886 
887 void QgsMapCanvas::clearTemporalCache()
888 {
889  if ( mCache )
890  {
891  bool invalidateLabels = false;
892  const QList<QgsMapLayer *> layerList = mapSettings().layers();
893  for ( QgsMapLayer *layer : layerList )
894  {
896  {
897  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
898  {
899  if ( vl->labelsEnabled() || vl->diagramsEnabled() )
900  invalidateLabels = true;
901  }
902 
904  continue;
905 
906  mCache->invalidateCacheForLayer( layer );
907  }
908  }
909 
910  if ( invalidateLabels )
911  {
912  mCache->clearCacheImage( QStringLiteral( "_labels_" ) );
913  mCache->clearCacheImage( QStringLiteral( "_preview_labels_" ) );
914  }
915  }
916 }
917 
918 void QgsMapCanvas::clearElevationCache()
919 {
920  if ( mCache )
921  {
922  bool invalidateLabels = false;
923  const QList<QgsMapLayer *> layerList = mapSettings().layers();
924  for ( QgsMapLayer *layer : layerList )
925  {
927  {
928  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
929  {
930  if ( vl->labelsEnabled() || vl->diagramsEnabled() )
931  invalidateLabels = true;
932  }
933 
935  continue;
936 
937  mCache->invalidateCacheForLayer( layer );
938  }
939  }
940 
941  if ( invalidateLabels )
942  {
943  mCache->clearCacheImage( QStringLiteral( "_labels_" ) );
944  mCache->clearCacheImage( QStringLiteral( "_preview_labels_" ) );
945  }
946  }
947 }
948 
949 void QgsMapCanvas::showContextMenu( QgsMapMouseEvent *event )
950 {
951  const QgsPointXY mapPoint = event->originalMapPoint();
952 
953  QMenu menu;
954 
955  QMenu *copyCoordinateMenu = new QMenu( tr( "Copy Coordinate" ), &menu );
956  copyCoordinateMenu->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );
957 
958  auto addCoordinateFormat = [ &, this]( const QString identifier, const QgsCoordinateReferenceSystem & crs )
959  {
960  const QgsCoordinateTransform ct( mSettings.destinationCrs(), crs, mSettings.transformContext() );
961  try
962  {
963  const QgsPointXY transformedPoint = ct.transform( mapPoint );
964 
965  // calculate precision based on visible map extent -- if user is zoomed in, we get better precision!
966  int displayPrecision = 0;
967  try
968  {
969  QgsCoordinateTransform extentTransform = ct;
970  extentTransform.setBallparkTransformsAreAppropriate( true );
971  QgsRectangle extentReproj = extentTransform.transformBoundingBox( extent() );
972  const double mapUnitsPerPixel = ( extentReproj.width() / width() + extentReproj.height() / height() ) * 0.5;
973  if ( mapUnitsPerPixel > 10 )
974  displayPrecision = 0;
975  else if ( mapUnitsPerPixel > 1 )
976  displayPrecision = 1;
977  else if ( mapUnitsPerPixel > 0.1 )
978  displayPrecision = 2;
979  else if ( mapUnitsPerPixel > 0.01 )
980  displayPrecision = 3;
981  else if ( mapUnitsPerPixel > 0.001 )
982  displayPrecision = 4;
983  else if ( mapUnitsPerPixel > 0.0001 )
984  displayPrecision = 5;
985  else if ( mapUnitsPerPixel > 0.00001 )
986  displayPrecision = 6;
987  else if ( mapUnitsPerPixel > 0.000001 )
988  displayPrecision = 7;
989  else if ( mapUnitsPerPixel > 0.0000001 )
990  displayPrecision = 8;
991  else
992  displayPrecision = 9;
993  }
994  catch ( QgsCsException & )
995  {
996  displayPrecision = crs.mapUnits() == QgsUnitTypes::DistanceDegrees ? 5 : 3;
997  }
998 
999  QAction *copyCoordinateAction = new QAction( QStringLiteral( "%3 (%1, %2)" ).arg(
1000  QString::number( transformedPoint.x(), 'f', displayPrecision ),
1001  QString::number( transformedPoint.y(), 'f', displayPrecision ),
1002  identifier ), &menu );
1003 
1004  connect( copyCoordinateAction, &QAction::triggered, this, [displayPrecision, transformedPoint]
1005  {
1006  QClipboard *clipboard = QApplication::clipboard();
1007 
1008  const QString coordinates = QString::number( transformedPoint.x(), 'f', displayPrecision ) + ',' + QString::number( transformedPoint.y(), 'f', displayPrecision );
1009 
1010  //if we are on x11 system put text into selection ready for middle button pasting
1011  if ( clipboard->supportsSelection() )
1012  {
1013  clipboard->setText( coordinates, QClipboard::Selection );
1014  }
1015  clipboard->setText( coordinates, QClipboard::Clipboard );
1016 
1017  } );
1018  copyCoordinateMenu->addAction( copyCoordinateAction );
1019  }
1020  catch ( QgsCsException & )
1021  {
1022 
1023  }
1024  };
1025 
1026  addCoordinateFormat( tr( "Map CRS — %1" ).arg( mSettings.destinationCrs().userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ) ), mSettings.destinationCrs() );
1027  if ( mSettings.destinationCrs() != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
1028  addCoordinateFormat( tr( "WGS84" ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
1029 
1030  QgsSettings settings;
1031  const QString customCrsString = settings.value( QStringLiteral( "qgis/custom_coordinate_crs" ) ).toString();
1032  if ( !customCrsString.isEmpty() )
1033  {
1034  QgsCoordinateReferenceSystem customCrs( customCrsString );
1035  if ( customCrs != mSettings.destinationCrs() && customCrs != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
1036  {
1037  addCoordinateFormat( customCrs.userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ), customCrs );
1038  }
1039  }
1040  copyCoordinateMenu->addSeparator();
1041  QAction *setCustomCrsAction = new QAction( tr( "Set Custom CRS…" ), &menu );
1042  connect( setCustomCrsAction, &QAction::triggered, this, [ = ]
1043  {
1044  QgsProjectionSelectionDialog selector( this );
1045  selector.setCrs( QgsCoordinateReferenceSystem( customCrsString ) );
1046  if ( selector.exec() )
1047  {
1048  QgsSettings().setValue( QStringLiteral( "qgis/custom_coordinate_crs" ), selector.crs().authid().isEmpty() ? selector.crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : selector.crs().authid() );
1049  }
1050  } );
1051  copyCoordinateMenu->addAction( setCustomCrsAction );
1052 
1053  menu.addMenu( copyCoordinateMenu );
1054 
1055  if ( mMapTool )
1056  if ( !mapTool()->populateContextMenuWithEvent( &menu, event ) )
1057  mMapTool->populateContextMenu( &menu );
1058 
1059  emit contextMenuAboutToShow( &menu, event );
1060 
1061  menu.exec( event->globalPos() );
1062 }
1063 
1064 void QgsMapCanvas::notifyRendererErrors( const QgsMapRendererJob::Errors &errors )
1065 {
1066  const QDateTime currentTime = QDateTime::currentDateTime();
1067 
1068  // remove errors too old
1069  for ( const QgsMapRendererJob::Error &error : errors )
1070  {
1071  const QString errorKey = error.layerID + ':' + error.message;
1072  if ( mRendererErrors.contains( errorKey ) )
1073  {
1074  const QDateTime sameErrorTime = mRendererErrors.value( errorKey );
1075 
1076  if ( sameErrorTime.secsTo( currentTime ) < 60 )
1077  continue;
1078  }
1079 
1080  mRendererErrors[errorKey] = currentTime;
1081 
1082  if ( QgsMapLayer *layer = QgsProject::instance()->mapLayer( error.layerID ) )
1083  emit renderErrorOccurred( error.message, layer );
1084  }
1085 }
1086 
1087 void QgsMapCanvas::updateDevicePixelFromScreen()
1088 {
1089  mSettings.setDevicePixelRatio( devicePixelRatio() );
1090  // TODO: QGIS 4 -> always respect screen dpi
1092  {
1093  if ( window()->windowHandle() )
1094  {
1095  mSettings.setOutputDpi( window()->windowHandle()->screen()->physicalDotsPerInch() );
1096  mSettings.setDpiTarget( window()->windowHandle()->screen()->physicalDotsPerInch() );
1097  }
1098  }
1099  else
1100  {
1101  // Fallback: compatibility with QGIS <= 3.20; always assume low dpi screens
1102  mSettings.setOutputDpi( window()->windowHandle()->screen()->logicalDotsPerInch() );
1103  mSettings.setDpiTarget( window()->windowHandle()->screen()->logicalDotsPerInch() );
1104  }
1105 }
1106 
1107 void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange )
1108 {
1109  if ( temporalRange() == dateTimeRange )
1110  return;
1111 
1112  mSettings.setTemporalRange( dateTimeRange );
1113  mSettings.setIsTemporal( dateTimeRange.begin().isValid() || dateTimeRange.end().isValid() );
1114 
1115  emit temporalRangeChanged();
1116 
1117  // we need to discard any previously cached images which have temporal properties enabled, so that these will be updated when
1118  // the canvas is redrawn
1119  if ( !mJob )
1120  clearTemporalCache();
1121 
1122  autoRefreshTriggered();
1123 }
1124 
1125 const QgsDateTimeRange &QgsMapCanvas::temporalRange() const
1126 {
1127  return mSettings.temporalRange();
1128 }
1129 
1131 {
1132  mInteractionBlockers.append( blocker );
1133 }
1134 
1136 {
1137  mInteractionBlockers.removeAll( blocker );
1138 }
1139 
1141 {
1142  for ( const QgsMapCanvasInteractionBlocker *block : mInteractionBlockers )
1143  {
1144  if ( block->blockCanvasInteraction( interaction ) )
1145  return false;
1146  }
1147  return true;
1148 }
1149 
1150 void QgsMapCanvas::mapUpdateTimeout()
1151 {
1152  if ( mJob )
1153  {
1154  const QImage &img = mJob->renderedImage();
1155  mMap->setContent( img, imageRect( img, mSettings ) );
1156  }
1157 }
1158 
1160 {
1161  if ( mJob )
1162  {
1163  QgsDebugMsgLevel( QStringLiteral( "CANVAS stop rendering!" ), 2 );
1164  mJobCanceled = true;
1165  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
1166  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
1167  mJob->cancelWithoutBlocking();
1168  mJob = nullptr;
1169  emit mapRefreshCanceled();
1170  }
1171  stopPreviewJobs();
1172 }
1173 
1174 //the format defaults to "PNG" if not specified
1175 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
1176 {
1177  QPainter painter;
1178  QImage image;
1179 
1180  //
1181  //check if the optional QPaintDevice was supplied
1182  //
1183  if ( theQPixmap )
1184  {
1185  image = theQPixmap->toImage();
1186  painter.begin( &image );
1187 
1188  // render
1189  QgsMapRendererCustomPainterJob job( mSettings, &painter );
1190  job.start();
1191  job.waitForFinished();
1192  emit renderComplete( &painter );
1193  }
1194  else //use the map view
1195  {
1196  image = mMap->contentImage().copy();
1197  painter.begin( &image );
1198  }
1199 
1200  // draw annotations
1201  QStyleOptionGraphicsItem option;
1202  option.initFrom( this );
1203  QGraphicsItem *item = nullptr;
1204  QListIterator<QGraphicsItem *> i( items() );
1205  i.toBack();
1206  while ( i.hasPrevious() )
1207  {
1208  item = i.previous();
1209 
1210  if ( !( item && dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) ) )
1211  {
1212  continue;
1213  }
1214 
1215  QgsScopedQPainterState painterState( &painter );
1216 
1217  QPointF itemScenePos = item->scenePos();
1218  painter.translate( itemScenePos.x(), itemScenePos.y() );
1219 
1220  item->paint( &painter, &option );
1221  }
1222 
1223  painter.end();
1224  image.save( fileName, format.toLocal8Bit().data() );
1225 
1226  QFileInfo myInfo = QFileInfo( fileName );
1227 
1228  // build the world file name
1229  QString outputSuffix = myInfo.suffix();
1230  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.completeBaseName() + '.'
1231  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
1232  QFile myWorldFile( myWorldFileName );
1233  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
1234  {
1235  return;
1236  }
1237  QTextStream myStream( &myWorldFile );
1239 }
1240 
1242 {
1243  return mapSettings().visibleExtent();
1244 }
1245 
1247 {
1248  return QgsMapLayerUtils::combinedExtent( mSettings.layers(), mapSettings().destinationCrs(), QgsProject::instance()->transformContext() );
1249 }
1250 
1252 {
1254  QgsCoordinateTransform ct( extent.crs(), mapSettings().destinationCrs(), mProject ? mProject->transformContext() : QgsProject::instance()->transformContext() );
1256  QgsRectangle rect;
1257  try
1258  {
1259  rect = ct.transformBoundingBox( extent );
1260  }
1261  catch ( QgsCsException & )
1262  {
1263  rect = mapSettings().fullExtent();
1264  }
1265 
1266  return rect;
1267 }
1268 
1269 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
1270 {
1271  QgsRectangle current = extent();
1272 
1273  if ( ( r == current ) && magnified )
1274  return;
1275 
1276  if ( r.isEmpty() )
1277  {
1278  if ( !mSettings.hasValidSettings() )
1279  {
1280  // we can't even just move the map center
1281  QgsDebugMsgLevel( QStringLiteral( "Empty extent - ignoring" ), 2 );
1282  return;
1283  }
1284 
1285  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
1286  QgsDebugMsgLevel( QStringLiteral( "Empty extent - keeping old scale with new center!" ), 2 );
1287  setCenter( r.center() );
1288  }
1289  else
1290  {
1291  // If scale is locked we need to maintain the current scale, so we
1292  // - magnify and recenter the map
1293  // - restore locked scale
1294  if ( mScaleLocked && magnified )
1295  {
1296  ScaleRestorer restorer( this );
1297  const double ratio { extent().width() / extent().height() };
1298  const double factor { r.width() / r.height() > ratio ? extent().width() / r.width() : extent().height() / r.height() };
1299  const double scaleFactor { std::clamp( mSettings.magnificationFactor() * factor, QgsGuiUtils::CANVAS_MAGNIFICATION_MIN, QgsGuiUtils::CANVAS_MAGNIFICATION_MAX ) };
1300  const QgsPointXY newCenter { r.center() };
1301  mSettings.setMagnificationFactor( scaleFactor, &newCenter );
1302  emit magnificationChanged( scaleFactor );
1303  }
1304  else
1305  {
1306  mSettings.setExtent( r, magnified );
1307  }
1308  }
1309  emit extentsChanged();
1310  updateScale();
1311 
1312  //clear all extent items after current index
1313  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
1314  {
1315  mLastExtent.removeAt( i );
1316  }
1317 
1318  if ( !mLastExtent.isEmpty() && mLastExtent.last() != extent() )
1319  {
1320  mLastExtent.append( extent() );
1321  }
1322 
1323  // adjust history to no more than 100
1324  if ( mLastExtent.size() > 100 )
1325  {
1326  mLastExtent.removeAt( 0 );
1327  }
1328 
1329  // the last item is the current extent
1330  mLastExtentIndex = mLastExtent.size() - 1;
1331 
1332  // update controls' enabled state
1333  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1334  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1335 }
1336 
1338 {
1339  QgsRectangle canvasExtent = extent;
1340  if ( extent.crs() != mapSettings().destinationCrs() )
1341  {
1342  QgsCoordinateTransform ct( extent.crs(), mapSettings().destinationCrs(), QgsProject::instance() );
1344  canvasExtent = ct.transformBoundingBox( extent );
1345 
1346  if ( canvasExtent.isEmpty() )
1347  {
1348  return false;
1349  }
1350  }
1351 
1352  setExtent( canvasExtent, true );
1353  return true;
1354 }
1355 
1357 {
1358  const QgsRectangle r = mapSettings().extent();
1359  const double xMin = center.x() - r.width() / 2.0;
1360  const double yMin = center.y() - r.height() / 2.0;
1361  const QgsRectangle rect(
1362  xMin, yMin,
1363  xMin + r.width(), yMin + r.height()
1364  );
1365  if ( ! rect.isEmpty() )
1366  {
1367  setExtent( rect, true );
1368  }
1369 } // setCenter
1370 
1372 {
1374  return r.center();
1375 }
1376 
1377 QgsPointXY QgsMapCanvas::cursorPoint() const
1378 {
1379  return mCursorPoint;
1380 }
1381 
1383 {
1384  return mapSettings().rotation();
1385 } // rotation
1386 
1387 void QgsMapCanvas::setRotation( double degrees )
1388 {
1389  double current = rotation();
1390 
1391  if ( qgsDoubleNear( degrees, current ) )
1392  return;
1393 
1394  mSettings.setRotation( degrees );
1395  emit rotationChanged( degrees );
1396  emit extentsChanged(); // visible extent changes with rotation
1397 } // setRotation
1398 
1399 
1401 {
1402  emit scaleChanged( mapSettings().scale() );
1403 }
1404 
1406 {
1408  // If the full extent is an empty set, don't do the zoom
1409  if ( !extent.isEmpty() )
1410  {
1411  // Add a 5% margin around the full extent
1412  extent.scale( 1.05 );
1413  setExtent( extent );
1414  }
1415  refresh();
1416 }
1417 
1419 {
1421 
1422  // If the full extent is an empty set, don't do the zoom
1423  if ( !extent.isEmpty() )
1424  {
1425  // Add a 5% margin around the full extent
1426  extent.scale( 1.05 );
1427  setExtent( extent );
1428  }
1429  refresh();
1430 }
1431 
1433 {
1434  if ( mLastExtentIndex > 0 )
1435  {
1436  mLastExtentIndex--;
1437  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
1438  emit extentsChanged();
1439  updateScale();
1440  refresh();
1441  // update controls' enabled state
1442  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1443  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1444  }
1445 
1446 } // zoomToPreviousExtent
1447 
1449 {
1450  if ( mLastExtentIndex < mLastExtent.size() - 1 )
1451  {
1452  mLastExtentIndex++;
1453  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
1454  emit extentsChanged();
1455  updateScale();
1456  refresh();
1457  // update controls' enabled state
1458  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1459  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1460  }
1461 }// zoomToNextExtent
1462 
1464 {
1465  mLastExtent.clear(); // clear the zoom history list
1466  mLastExtent.append( extent() ) ; // set the current extent in the list
1467  mLastExtentIndex = mLastExtent.size() - 1;
1468  // update controls' enabled state
1469  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1470  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1471 }// clearExtentHistory
1472 
1473 QgsRectangle QgsMapCanvas::optimalExtentForPointLayer( QgsVectorLayer *layer, const QgsPointXY &center, int scaleFactor )
1474 {
1475  QgsRectangle rect( center, center );
1476 
1477  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
1478  {
1479  QgsPointXY centerLayerCoordinates = mSettings.mapToLayerCoordinates( layer, center );
1480  QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, &centerLayerCoordinates );
1482  QgsFeatureIterator fit = layer->getFeatures( req );
1483  QgsFeature f;
1484  QgsPointXY closestPoint;
1485  double closestSquaredDistance = pow( extentRect.width(), 2.0 ) + pow( extentRect.height(), 2.0 );
1486  bool pointFound = false;
1487  while ( fit.nextFeature( f ) )
1488  {
1489  QgsPointXY point = f.geometry().asPoint();
1490  double sqrDist = point.sqrDist( centerLayerCoordinates );
1491  if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits<double>::epsilon() )
1492  continue;
1493  pointFound = true;
1494  closestPoint = point;
1495  closestSquaredDistance = sqrDist;
1496  }
1497  if ( pointFound )
1498  {
1499  // combine selected point with closest point and scale this rect
1500  rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) );
1501  rect.scale( scaleFactor, &center );
1502  }
1503  }
1504  return rect;
1505 }
1506 
1508 {
1509  QgsTemporaryCursorOverride cursorOverride( Qt::WaitCursor );
1510 
1511  if ( !layer )
1512  {
1513  // use current layer by default
1514  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1515  }
1516 
1517  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1518  return;
1519 
1520  QgsRectangle rect = layer->boundingBoxOfSelected();
1521  if ( rect.isNull() )
1522  {
1523  cursorOverride.release();
1524  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1525  return;
1526  }
1527 
1528  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1529 
1530  // zoom in if point cannot be distinguished from others
1531  // also check that rect is empty, as it might not in case of multi points
1532  if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1533  {
1534  rect = optimalExtentForPointLayer( layer, rect.center() );
1535  }
1536  zoomToFeatureExtent( rect );
1537 }
1538 
1539 void QgsMapCanvas::zoomToSelected( const QList<QgsMapLayer *> &layers )
1540 {
1541  QgsRectangle rect;
1542  rect.setMinimal();
1543  QgsRectangle selectionExtent;
1544  selectionExtent.setMinimal();
1545 
1546  for ( QgsMapLayer *mapLayer : layers )
1547  {
1548  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mapLayer );
1549 
1550  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1551  continue;
1552 
1553  rect = layer->boundingBoxOfSelected();
1554 
1555  if ( rect.isNull() )
1556  continue;
1557 
1558  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1559 
1560  if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1561  rect = optimalExtentForPointLayer( layer, rect.center() );
1562 
1563  selectionExtent.combineExtentWith( rect );
1564  }
1565 
1566  if ( selectionExtent.isNull() )
1567  {
1568  emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1569  return;
1570  }
1571 
1572  zoomToFeatureExtent( selectionExtent );
1573 }
1574 
1576 {
1577  return mSettings.zRange();
1578 }
1579 
1581 {
1582  if ( zRange() == range )
1583  return;
1584 
1585  mSettings.setZRange( range );
1586 
1587  emit zRangeChanged();
1588 
1589  // we need to discard any previously cached images which are elevation aware, so that these will be updated when
1590  // the canvas is redrawn
1591  if ( !mJob )
1592  clearElevationCache();
1593 
1594  autoRefreshTriggered();
1595 }
1596 
1598 {
1599  // no selected features, only one selected point feature
1600  //or two point features with the same x- or y-coordinates
1601  if ( rect.isEmpty() )
1602  {
1603  // zoom in
1604  QgsPointXY c = rect.center();
1605  rect = extent();
1606  rect.scale( 1.0, &c );
1607  }
1608  //zoom to an area
1609  else
1610  {
1611  // Expand rect to give a bit of space around the selected
1612  // objects so as to keep them clear of the map boundaries
1613  // The same 5% should apply to all margins.
1614  rect.scale( 1.05 );
1615  }
1616 
1617  setExtent( rect );
1618  refresh();
1619 }
1620 
1622 {
1623  if ( !layer )
1624  {
1625  return;
1626  }
1627 
1628  QgsRectangle bbox;
1629  QString errorMsg;
1630  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1631  {
1632  if ( bbox.isEmpty() )
1633  {
1634  bbox = optimalExtentForPointLayer( layer, bbox.center() );
1635  }
1636  zoomToFeatureExtent( bbox );
1637  }
1638  else
1639  {
1640  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, Qgis::MessageLevel::Warning );
1641  }
1642 
1643 }
1644 
1645 void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter )
1646 {
1647  if ( !layer )
1648  {
1649  return;
1650  }
1651 
1652  QgsRectangle bbox;
1653  QString errorMsg;
1654  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1655  {
1656  if ( alwaysRecenter || !mapSettings().extent().contains( bbox ) )
1657  setCenter( bbox.center() );
1658  refresh();
1659  }
1660  else
1661  {
1662  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, Qgis::MessageLevel::Warning );
1663  }
1664 }
1665 
1666 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1667 {
1668  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1669  bbox.setMinimal();
1670  QgsFeature fet;
1671  int featureCount = 0;
1672  errorMsg.clear();
1673 
1674  while ( it.nextFeature( fet ) )
1675  {
1676  QgsGeometry geom = fet.geometry();
1677  if ( geom.isNull() )
1678  {
1679  errorMsg = tr( "Feature does not have a geometry" );
1680  }
1681  else if ( geom.constGet()->isEmpty() )
1682  {
1683  errorMsg = tr( "Feature geometry is empty" );
1684  }
1685  if ( !errorMsg.isEmpty() )
1686  {
1687  return false;
1688  }
1690  bbox.combineExtentWith( r );
1691  featureCount++;
1692  }
1693 
1694  if ( featureCount != ids.count() )
1695  {
1696  errorMsg = tr( "Feature not found" );
1697  return false;
1698  }
1699 
1700  return true;
1701 }
1702 
1704 {
1705  if ( !layer )
1706  {
1707  // use current layer by default
1708  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1709  }
1710 
1711  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1712  return;
1713 
1714  QgsRectangle rect = layer->boundingBoxOfSelected();
1715  if ( rect.isNull() )
1716  {
1717  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1718  return;
1719  }
1720 
1721  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1722  setCenter( rect.center() );
1723  refresh();
1724 }
1725 
1726 void QgsMapCanvas::panToSelected( const QList<QgsMapLayer *> &layers )
1727 {
1728  QgsRectangle rect;
1729  rect.setMinimal();
1730  QgsRectangle selectionExtent;
1731  selectionExtent.setMinimal();
1732 
1733  for ( QgsMapLayer *mapLayer : layers )
1734  {
1735  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mapLayer );
1736 
1737  if ( !layer || !layer->isSpatial() || layer->selectedFeatureCount() == 0 )
1738  continue;
1739 
1740  rect = layer->boundingBoxOfSelected();
1741 
1742  if ( rect.isNull() )
1743  continue;
1744 
1745  rect = mapSettings().layerExtentToOutputExtent( layer, rect );
1746 
1747  if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1748  rect = optimalExtentForPointLayer( layer, rect.center() );
1749 
1750  selectionExtent.combineExtentWith( rect );
1751  }
1752 
1753  if ( selectionExtent.isNull() )
1754  {
1755  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1756  return;
1757  }
1758 
1759  setCenter( selectionExtent.center() );
1760  refresh();
1761 }
1762 
1764  const QColor &color1, const QColor &color2,
1765  int flashes, int duration )
1766 {
1767  if ( !layer )
1768  {
1769  return;
1770  }
1771 
1772  QList< QgsGeometry > geoms;
1773 
1774  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1775  QgsFeature fet;
1776  while ( it.nextFeature( fet ) )
1777  {
1778  if ( !fet.hasGeometry() )
1779  continue;
1780  geoms << fet.geometry();
1781  }
1782 
1783  flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
1784 }
1785 
1786 void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
1787 {
1788  if ( geometries.isEmpty() )
1789  return;
1790 
1791  QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
1792  QgsRubberBand *rb = new QgsRubberBand( this, geomType );
1793  for ( const QgsGeometry &geom : geometries )
1794  rb->addGeometry( geom, crs, false );
1795  rb->updatePosition();
1796  rb->update();
1797 
1798  if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
1799  {
1800  rb->setWidth( 2 );
1801  rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
1802  }
1803  if ( geomType == QgsWkbTypes::PointGeometry )
1805 
1806  QColor startColor = color1;
1807  if ( !startColor.isValid() )
1808  {
1809  if ( geomType == QgsWkbTypes::PolygonGeometry )
1810  {
1811  startColor = rb->fillColor();
1812  }
1813  else
1814  {
1815  startColor = rb->strokeColor();
1816  }
1817  startColor.setAlpha( 255 );
1818  }
1819  QColor endColor = color2;
1820  if ( !endColor.isValid() )
1821  {
1822  endColor = startColor;
1823  endColor.setAlpha( 0 );
1824  }
1825 
1826 
1827  QVariantAnimation *animation = new QVariantAnimation( this );
1828  connect( animation, &QVariantAnimation::finished, this, [animation, rb]
1829  {
1830  animation->deleteLater();
1831  delete rb;
1832  } );
1833  connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
1834  {
1835  QColor c = value.value<QColor>();
1836  if ( geomType == QgsWkbTypes::PolygonGeometry )
1837  {
1838  rb->setFillColor( c );
1839  }
1840  else
1841  {
1842  rb->setStrokeColor( c );
1843  QColor c = rb->secondaryStrokeColor();
1844  c.setAlpha( c.alpha() );
1845  rb->setSecondaryStrokeColor( c );
1846  }
1847  rb->update();
1848  } );
1849 
1850  animation->setDuration( duration * flashes );
1851  animation->setStartValue( endColor );
1852  double midStep = 0.2 / flashes;
1853  for ( int i = 0; i < flashes; ++i )
1854  {
1855  double start = static_cast< double >( i ) / flashes;
1856  animation->setKeyValueAt( start + midStep, startColor );
1857  double end = static_cast< double >( i + 1 ) / flashes;
1858  if ( !qgsDoubleNear( end, 1.0 ) )
1859  animation->setKeyValueAt( end, endColor );
1860  }
1861  animation->setEndValue( endColor );
1862  animation->start();
1863 }
1864 
1865 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1866 {
1867  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1868  {
1869  emit keyPressed( e );
1870  return;
1871  }
1872 
1873  // Don't want to interfer with mouse events
1874  if ( ! mCanvasProperties->mouseButtonDown )
1875  {
1876  // this is backwards, but we can't change now without breaking api because
1877  // forever QgsMapTools have had to explicitly mark events as ignored in order to
1878  // indicate that they've consumed the event and that the default behavior should not
1879  // be applied..!
1880  e->accept();
1881  if ( mMapTool )
1882  {
1883  mMapTool->keyPressEvent( e );
1884  if ( !e->isAccepted() ) // map tool consumed event
1885  return;
1886  }
1887 
1888  QgsRectangle currentExtent = mapSettings().visibleExtent();
1889  double dx = std::fabs( currentExtent.width() / 4 );
1890  double dy = std::fabs( currentExtent.height() / 4 );
1891 
1892  switch ( e->key() )
1893  {
1894  case Qt::Key_Left:
1895  QgsDebugMsgLevel( QStringLiteral( "Pan left" ), 2 );
1896  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1897  refresh();
1898  break;
1899 
1900  case Qt::Key_Right:
1901  QgsDebugMsgLevel( QStringLiteral( "Pan right" ), 2 );
1902  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1903  refresh();
1904  break;
1905 
1906  case Qt::Key_Up:
1907  QgsDebugMsgLevel( QStringLiteral( "Pan up" ), 2 );
1908  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1909  refresh();
1910  break;
1911 
1912  case Qt::Key_Down:
1913  QgsDebugMsgLevel( QStringLiteral( "Pan down" ), 2 );
1914  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1915  refresh();
1916  break;
1917 
1918  case Qt::Key_Space:
1919  QgsDebugMsgLevel( QStringLiteral( "Pressing pan selector" ), 2 );
1920 
1921  //mCanvasProperties->dragging = true;
1922  if ( ! e->isAutoRepeat() )
1923  {
1924  mTemporaryCursorOverride.reset( new QgsTemporaryCursorOverride( Qt::ClosedHandCursor ) );
1925  mCanvasProperties->panSelectorDown = true;
1926  panActionStart( mCanvasProperties->mouseLastXY );
1927  }
1928  break;
1929 
1930  case Qt::Key_PageUp:
1931  QgsDebugMsgLevel( QStringLiteral( "Zoom in" ), 2 );
1932  zoomIn();
1933  break;
1934 
1935  case Qt::Key_PageDown:
1936  QgsDebugMsgLevel( QStringLiteral( "Zoom out" ), 2 );
1937  zoomOut();
1938  break;
1939 
1940 #if 0
1941  case Qt::Key_P:
1942  mUseParallelRendering = !mUseParallelRendering;
1943  refresh();
1944  break;
1945 
1946  case Qt::Key_S:
1947  mDrawRenderingStats = !mDrawRenderingStats;
1948  refresh();
1949  break;
1950 #endif
1951 
1952  default:
1953  // Pass it on
1954  if ( !mMapTool )
1955  {
1956  e->ignore();
1957  QgsDebugMsgLevel( "Ignoring key: " + QString::number( e->key() ), 2 );
1958  }
1959  }
1960  }
1961 
1962  emit keyPressed( e );
1963 }
1964 
1965 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1966 {
1967  QgsDebugMsgLevel( QStringLiteral( "keyRelease event" ), 2 );
1968 
1969  switch ( e->key() )
1970  {
1971  case Qt::Key_Space:
1972  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1973  {
1974  QgsDebugMsgLevel( QStringLiteral( "Releasing pan selector" ), 2 );
1975  mTemporaryCursorOverride.reset();
1976  mCanvasProperties->panSelectorDown = false;
1977  panActionEnd( mCanvasProperties->mouseLastXY );
1978  }
1979  break;
1980 
1981  default:
1982  // Pass it on
1983  if ( mMapTool )
1984  {
1985  mMapTool->keyReleaseEvent( e );
1986  }
1987  else e->ignore();
1988 
1989  QgsDebugMsgLevel( "Ignoring key release: " + QString::number( e->key() ), 2 );
1990  }
1991 
1992  emit keyReleased( e );
1993 
1994 } //keyReleaseEvent()
1995 
1996 
1998 {
1999  // call handler of current map tool
2000  if ( mMapTool )
2001  {
2002  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2003  mMapTool->canvasDoubleClickEvent( me.get() );
2004  }
2005 }// mouseDoubleClickEvent
2006 
2007 
2008 void QgsMapCanvas::beginZoomRect( QPoint pos )
2009 {
2010  mZoomRect.setRect( 0, 0, 0, 0 );
2011  mTemporaryCursorOverride.reset( new QgsTemporaryCursorOverride( mZoomCursor ) );
2012  mZoomDragging = true;
2013  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
2014  QColor color( Qt::blue );
2015  color.setAlpha( 63 );
2016  mZoomRubberBand->setColor( color );
2017  mZoomRect.setTopLeft( pos );
2018 }
2019 
2020 void QgsMapCanvas::endZoomRect( QPoint pos )
2021 {
2022  mZoomDragging = false;
2023  mZoomRubberBand.reset( nullptr );
2024  mTemporaryCursorOverride.reset();
2025 
2026  // store the rectangle
2027  mZoomRect.setRight( pos.x() );
2028  mZoomRect.setBottom( pos.y() );
2029 
2030  //account for bottom right -> top left dragging
2031  mZoomRect = mZoomRect.normalized();
2032 
2033  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
2034  {
2035  //probably a mistake - would result in huge zoom!
2036  return;
2037  }
2038 
2039  // set center and zoom
2040  const QSize &zoomRectSize = mZoomRect.size();
2041  const QSize &canvasSize = mSettings.outputSize();
2042  double sfx = static_cast< double >( zoomRectSize.width() ) / canvasSize.width();
2043  double sfy = static_cast< double >( zoomRectSize.height() ) / canvasSize.height();
2044  double sf = std::max( sfx, sfy );
2045 
2046  QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
2047 
2048  zoomByFactor( sf, &c );
2049  refresh();
2050 }
2051 
2052 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
2053 {
2054  // use shift+middle mouse button for zooming, map tools won't receive any events in that case
2055  if ( e->button() == Qt::MiddleButton &&
2056  e->modifiers() & Qt::ShiftModifier )
2057  {
2058  beginZoomRect( e->pos() );
2059  return;
2060  }
2061  //use middle mouse button for panning, map tools won't receive any events in that case
2062  else if ( e->button() == Qt::MiddleButton )
2063  {
2064  if ( !mCanvasProperties->panSelectorDown )
2065  {
2066  mCanvasProperties->panSelectorDown = true;
2067  mTemporaryCursorOverride.reset( new QgsTemporaryCursorOverride( Qt::ClosedHandCursor ) );
2068  panActionStart( mCanvasProperties->mouseLastXY );
2069  }
2070  }
2071  else
2072  {
2073  // call handler of current map tool
2074  if ( mMapTool )
2075  {
2076  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
2077  && e->modifiers() & Qt::ShiftModifier )
2078  {
2079  beginZoomRect( e->pos() );
2080  return;
2081  }
2082  else if ( mMapTool->flags() & QgsMapTool::ShowContextMenu && e->button() == Qt::RightButton )
2083  {
2084  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2085  showContextMenu( me.get() );
2086  return;
2087  }
2088  else
2089  {
2090  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2091  mMapTool->canvasPressEvent( me.get() );
2092  }
2093  }
2094  }
2095 
2096  if ( mCanvasProperties->panSelectorDown )
2097  {
2098  return;
2099  }
2100 
2101  mCanvasProperties->mouseButtonDown = true;
2102  mCanvasProperties->rubberStartPoint = e->pos();
2103 }
2104 
2105 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
2106 {
2107  // if using shift+middle mouse button for zooming, end zooming and return
2108  if ( mZoomDragging &&
2109  e->button() == Qt::MiddleButton )
2110  {
2111  endZoomRect( e->pos() );
2112  return;
2113  }
2114  //use middle mouse button for panning, map tools won't receive any events in that case
2115  else if ( e->button() == Qt::MiddleButton )
2116  {
2117  if ( mCanvasProperties->panSelectorDown )
2118  {
2119  mCanvasProperties->panSelectorDown = false;
2120  mTemporaryCursorOverride.reset();
2121  panActionEnd( mCanvasProperties->mouseLastXY );
2122  }
2123  }
2124  else if ( e->button() == Qt::BackButton )
2125  {
2127  return;
2128  }
2129  else if ( e->button() == Qt::ForwardButton )
2130  {
2131  zoomToNextExtent();
2132  return;
2133  }
2134  else
2135  {
2136  if ( mZoomDragging && e->button() == Qt::LeftButton )
2137  {
2138  endZoomRect( e->pos() );
2139  return;
2140  }
2141 
2142  // call handler of current map tool
2143  if ( mMapTool )
2144  {
2145  // right button was pressed in zoom tool? return to previous non zoom tool
2146  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
2147  {
2148  QgsDebugMsgLevel( QStringLiteral( "Right click in map tool zoom or pan, last tool is %1." ).arg(
2149  mLastNonZoomMapTool ? QStringLiteral( "not null" ) : QStringLiteral( "null" ) ), 2 );
2150 
2151  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
2152 
2153  // change to older non-zoom tool
2154  if ( mLastNonZoomMapTool
2155  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
2156  || ( vlayer && vlayer->isEditable() ) ) )
2157  {
2158  QgsMapTool *t = mLastNonZoomMapTool;
2159  mLastNonZoomMapTool = nullptr;
2160  setMapTool( t );
2161  }
2162  return;
2163  }
2164  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2165  mMapTool->canvasReleaseEvent( me.get() );
2166  }
2167  }
2168 
2169 
2170  mCanvasProperties->mouseButtonDown = false;
2171 
2172  if ( mCanvasProperties->panSelectorDown )
2173  return;
2174 
2175 }
2176 
2177 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
2178 {
2179  QGraphicsView::resizeEvent( e );
2180  mResizeTimer->start( 500 ); // in charge of refreshing canvas
2181 
2182  double oldScale = mSettings.scale();
2183  QSize lastSize = viewport()->size();
2184  mSettings.setOutputSize( lastSize );
2185 
2186  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
2187 
2188  moveCanvasContents( true );
2189 
2190  if ( mScaleLocked )
2191  {
2192  double scaleFactor = oldScale / mSettings.scale();
2193  QgsRectangle r = mSettings.extent();
2194  QgsPointXY center = r.center();
2195  r.scale( scaleFactor, &center );
2196  mSettings.setExtent( r );
2197  }
2198  else
2199  {
2200  updateScale();
2201  }
2202 
2203  emit extentsChanged();
2204 }
2205 
2206 void QgsMapCanvas::paintEvent( QPaintEvent *e )
2207 {
2208  // no custom event handling anymore
2209 
2210  QGraphicsView::paintEvent( e );
2211 } // paintEvent
2212 
2214 {
2215  if ( mBlockItemPositionUpdates )
2216  return;
2217 
2218  const QList<QGraphicsItem *> items = mScene->items();
2219  for ( QGraphicsItem *gi : items )
2220  {
2221  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( gi );
2222 
2223  if ( item )
2224  {
2225  item->updatePosition();
2226  }
2227  }
2228 }
2229 
2230 
2231 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
2232 {
2233  // Zoom the map canvas in response to a mouse wheel event. Moving the
2234  // wheel forward (away) from the user zooms in
2235 
2236  QgsDebugMsgLevel( "Wheel event delta " + QString::number( e->angleDelta().y() ), 2 );
2237 
2238  if ( mMapTool )
2239  {
2240  mMapTool->wheelEvent( e );
2241  if ( e->isAccepted() )
2242  return;
2243  }
2244 
2245  if ( e->angleDelta().y() == 0 )
2246  {
2247  e->accept();
2248  return;
2249  }
2250 
2251  double zoomFactor = e->angleDelta().y() > 0 ? 1. / zoomInFactor() : zoomOutFactor();
2252 
2253  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
2254  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
2255 
2256  if ( e->modifiers() & Qt::ControlModifier )
2257  {
2258  //holding ctrl while wheel zooming results in a finer zoom
2259  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
2260  }
2261 
2262  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
2263 
2264  // zoom map to mouse cursor by scaling
2265  QgsPointXY oldCenter = center();
2266 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
2267  QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->pos().x(), e->pos().y() ) );
2268 #else
2269  QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->position().x(), e->position().y() ) );
2270 #endif
2271  QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
2272  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
2273 
2274  zoomByFactor( signedWheelFactor, &newCenter );
2275  e->accept();
2276 }
2277 
2278 void QgsMapCanvas::setWheelFactor( double factor )
2279 {
2280  mWheelZoomFactor = factor;
2281 }
2282 
2284 {
2285  // magnification is alreday handled in zoomByFactor
2287 }
2288 
2290 {
2291  // magnification is alreday handled in zoomByFactor
2293 }
2294 
2295 void QgsMapCanvas::zoomScale( double newScale, bool ignoreScaleLock )
2296 {
2297  zoomByFactor( newScale / scale(), nullptr, ignoreScaleLock );
2298 }
2299 
2300 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
2301 {
2302  double scaleFactor = ( zoomIn ? zoomInFactor() : zoomOutFactor() );
2303 
2304  // transform the mouse pos to map coordinates
2306 
2307  if ( mScaleLocked )
2308  {
2309  ScaleRestorer restorer( this );
2311  }
2312  else
2313  {
2315  r.scale( scaleFactor, &center );
2316  setExtent( r, true );
2317  refresh();
2318  }
2319 }
2320 
2321 void QgsMapCanvas::setScaleLocked( bool isLocked )
2322 {
2323  if ( mScaleLocked != isLocked )
2324  {
2325  mScaleLocked = isLocked;
2326  emit scaleLockChanged( mScaleLocked );
2327  }
2328 }
2329 
2330 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
2331 {
2332  mCanvasProperties->mouseLastXY = e->pos();
2333 
2334  if ( mCanvasProperties->panSelectorDown )
2335  {
2336  panAction( e );
2337  }
2338  else if ( mZoomDragging )
2339  {
2340  mZoomRect.setBottomRight( e->pos() );
2341  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
2342  mZoomRubberBand->show();
2343  }
2344  else
2345  {
2346  // call handler of current map tool
2347  if ( mMapTool )
2348  {
2349  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2350  mMapTool->canvasMoveEvent( me.get() );
2351  }
2352  }
2353 
2354  // show x y on status bar (if we are mid pan operation, then the cursor point hasn't changed!)
2355  if ( !panOperationInProgress() )
2356  {
2357  mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
2358  emit xyCoordinates( mCursorPoint );
2359  }
2360 }
2361 
2362 void QgsMapCanvas::setMapTool( QgsMapTool *tool, bool clean )
2363 {
2364  if ( !tool )
2365  return;
2366 
2367  if ( mMapTool )
2368  {
2369  if ( clean )
2370  mMapTool->clean();
2371 
2372  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
2373  mMapTool->deactivate();
2374  }
2375 
2376  if ( ( tool->flags() & QgsMapTool::Transient )
2377  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
2378  {
2379  // if zoom or pan tool will be active, save old tool
2380  // to bring it back on right click
2381  // (but only if it wasn't also zoom or pan tool)
2382  mLastNonZoomMapTool = mMapTool;
2383  }
2384  else
2385  {
2386  mLastNonZoomMapTool = nullptr;
2387  }
2388 
2389  QgsMapTool *oldTool = mMapTool;
2390 
2391  // set new map tool and activate it
2392  mMapTool = tool;
2393  emit mapToolSet( mMapTool, oldTool );
2394  if ( mMapTool )
2395  {
2396  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
2397  mMapTool->activate();
2398  }
2399 
2400 } // setMapTool
2401 
2403 {
2404  if ( mMapTool && mMapTool == tool )
2405  {
2406  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
2407  QgsMapTool *oldTool = mMapTool;
2408  mMapTool = nullptr;
2409  oldTool->deactivate();
2410  emit mapToolSet( nullptr, oldTool );
2411  setCursor( Qt::ArrowCursor );
2412  }
2413 
2414  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
2415  {
2416  mLastNonZoomMapTool = nullptr;
2417  }
2418 }
2419 
2421 {
2422  mProject = project;
2423 }
2424 
2425 void QgsMapCanvas::setCanvasColor( const QColor &color )
2426 {
2427  if ( canvasColor() == color )
2428  return;
2429 
2430  // background of map's pixmap
2431  mSettings.setBackgroundColor( color );
2432 
2433  // background of the QGraphicsView
2434  QBrush bgBrush( color );
2435  setBackgroundBrush( bgBrush );
2436 #if 0
2437  QPalette palette;
2438  palette.setColor( backgroundRole(), color );
2439  setPalette( palette );
2440 #endif
2441 
2442  // background of QGraphicsScene
2443  mScene->setBackgroundBrush( bgBrush );
2444 
2445  refresh();
2446 
2447  emit canvasColorChanged();
2448 }
2449 
2451 {
2452  return mScene->backgroundBrush().color();
2453 }
2454 
2455 void QgsMapCanvas::setSelectionColor( const QColor &color )
2456 {
2457  if ( mSettings.selectionColor() == color )
2458  return;
2459 
2460  mSettings.setSelectionColor( color );
2461 
2462  if ( mCache )
2463  {
2464  bool hasSelectedFeatures = false;
2465  const auto layers = mSettings.layers();
2466  for ( QgsMapLayer *layer : layers )
2467  {
2468  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2469  if ( vlayer && vlayer->selectedFeatureCount() )
2470  {
2471  hasSelectedFeatures = true;
2472  break;
2473  }
2474  }
2475 
2476  if ( hasSelectedFeatures )
2477  {
2478  mCache->clear();
2479  refresh();
2480  }
2481  }
2482 }
2483 
2485 {
2486  return mSettings.selectionColor();
2487 }
2488 
2490 {
2491  return mapSettings().layers().size();
2492 }
2493 
2494 QList<QgsMapLayer *> QgsMapCanvas::layers( bool expandGroupLayers ) const
2495 {
2496  return mapSettings().layers( expandGroupLayers );
2497 }
2498 
2500 {
2501  // called when a layer has changed visibility setting
2502  refresh();
2503 }
2504 
2505 void QgsMapCanvas::freeze( bool frozen )
2506 {
2507  mFrozen = frozen;
2508 }
2509 
2511 {
2512  return mFrozen;
2513 }
2514 
2516 {
2517  return mapSettings().mapUnitsPerPixel();
2518 }
2519 
2521 {
2522  return mapSettings().mapUnits();
2523 }
2524 
2525 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
2526 {
2527  return mSettings.layerStyleOverrides();
2528 }
2529 
2530 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
2531 {
2532  if ( overrides == mSettings.layerStyleOverrides() )
2533  return;
2534 
2535  mSettings.setLayerStyleOverrides( overrides );
2536  clearCache();
2538 }
2539 
2540 void QgsMapCanvas::setTheme( const QString &theme )
2541 {
2542  if ( mTheme == theme )
2543  return;
2544 
2545  clearCache();
2546  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
2547  {
2548  mTheme.clear();
2549  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
2550  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
2551  emit themeChanged( QString() );
2552  }
2553  else
2554  {
2555  mTheme = theme;
2556  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
2557  emit themeChanged( theme );
2558  }
2559 }
2560 
2562 {
2563  mRenderFlag = flag;
2564 
2565  if ( mRenderFlag )
2566  {
2567  refresh();
2568  }
2569  else
2570  stopRendering();
2571 }
2572 
2573 #if 0
2574 void QgsMapCanvas::connectNotify( const char *signal )
2575 {
2576  Q_UNUSED( signal )
2577  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
2578 } //connectNotify
2579 #endif
2580 
2581 void QgsMapCanvas::layerRepaintRequested( bool deferred )
2582 {
2583  if ( !deferred )
2584  refresh();
2585 }
2586 
2587 void QgsMapCanvas::autoRefreshTriggered()
2588 {
2589  if ( mJob )
2590  {
2591  // canvas is currently being redrawn, so we defer the last requested
2592  // auto refresh until current rendering job finishes
2593  mRefreshAfterJob = true;
2594  return;
2595  }
2596 
2597  refresh();
2598 }
2599 
2600 void QgsMapCanvas::updateAutoRefreshTimer()
2601 {
2602  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
2603  // trigger a map refresh on this minimum interval
2604  int minAutoRefreshInterval = -1;
2605  const auto layers = mSettings.layers();
2606  for ( QgsMapLayer *layer : layers )
2607  {
2609  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
2610  }
2611 
2612  if ( minAutoRefreshInterval > 0 )
2613  {
2614  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
2615  mAutoRefreshTimer.start();
2616  }
2617  else
2618  {
2619  mAutoRefreshTimer.stop();
2620  }
2621 }
2622 
2623 void QgsMapCanvas::projectThemesChanged()
2624 {
2625  if ( mTheme.isEmpty() )
2626  return;
2627 
2628  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
2629  {
2630  // theme has been removed - stop following
2631  setTheme( QString() );
2632  }
2633 
2634 }
2635 
2637 {
2638  return mMapTool;
2639 }
2640 
2642 {
2643  return mProject;
2644 }
2645 
2646 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
2647 {
2648  // move map image and other items to standard position
2649  moveCanvasContents( true ); // true means reset
2650 
2651  // use start and end box points to calculate the extent
2652  QgsPointXY start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
2653  QgsPointXY end = getCoordinateTransform()->toMapCoordinates( releasePoint );
2654 
2655  // modify the center
2656  double dx = end.x() - start.x();
2657  double dy = end.y() - start.y();
2658  QgsPointXY c = center();
2659  c.set( c.x() - dx, c.y() - dy );
2660  setCenter( c );
2661 
2662  refresh();
2663 }
2664 
2665 void QgsMapCanvas::panActionStart( QPoint releasePoint )
2666 {
2667  mCanvasProperties->rubberStartPoint = releasePoint;
2668 
2669  mDa = QgsDistanceArea();
2670  mDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
2671  mDa.setSourceCrs( mapSettings().destinationCrs(), QgsProject::instance()->transformContext() );
2672 }
2673 
2674 void QgsMapCanvas::panAction( QMouseEvent *e )
2675 {
2676  Q_UNUSED( e )
2677 
2678  QgsPointXY currentMapPoint = getCoordinateTransform()->toMapCoordinates( e->pos() );
2679  QgsPointXY startMapPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
2680  try
2681  {
2682  emit panDistanceBearingChanged( mDa.measureLine( currentMapPoint, startMapPoint ), mDa.lengthUnits(), mDa.bearing( currentMapPoint, startMapPoint ) * 180 / M_PI );
2683  }
2684  catch ( QgsCsException & )
2685  {}
2686 
2687  // move all map canvas items
2689 }
2690 
2692 {
2693  QPoint pnt( 0, 0 );
2694  if ( !reset )
2695  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
2696 
2697  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
2698 }
2699 
2700 void QgsMapCanvas::dropEvent( QDropEvent *event )
2701 {
2702  if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
2703  {
2705  bool allHandled = true;
2706  for ( const QgsMimeDataUtils::Uri &uri : lst )
2707  {
2708  bool handled = false;
2709  for ( QgsCustomDropHandler *handler : std::as_const( mDropHandlers ) )
2710  {
2711  if ( handler && handler->customUriProviderKey() == uri.providerKey )
2712  {
2713  if ( handler->handleCustomUriCanvasDrop( uri, this ) )
2714  {
2715  handled = true;
2716  break;
2717  }
2718  }
2719  }
2720  if ( !handled )
2721  allHandled = false;
2722  }
2723  if ( allHandled )
2724  event->accept();
2725  else
2726  event->ignore();
2727  }
2728  else
2729  {
2730  event->ignore();
2731  }
2732 }
2733 
2734 void QgsMapCanvas::showEvent( QShowEvent *event )
2735 {
2736  Q_UNUSED( event )
2737  updateDevicePixelFromScreen();
2738  // keep device pixel ratio up to date on screen or resolution change
2739  if ( window()->windowHandle() )
2740  {
2741  connect( window()->windowHandle(), &QWindow::screenChanged, this, [ = ]( QScreen * )
2742  {
2743  disconnect( mScreenDpiChangedConnection );
2744  mScreenDpiChangedConnection = connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
2745  updateDevicePixelFromScreen();
2746  } );
2747 
2748  mScreenDpiChangedConnection = connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
2749  }
2750 }
2751 
2753 {
2754  return mCanvasProperties->mouseLastXY;
2755 }
2756 
2757 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
2758 {
2759  if ( !mPreviewEffect )
2760  {
2761  return;
2762  }
2763 
2764  mPreviewEffect->setEnabled( previewEnabled );
2765 }
2766 
2768 {
2769  if ( !mPreviewEffect )
2770  {
2771  return false;
2772  }
2773 
2774  return mPreviewEffect->isEnabled();
2775 }
2776 
2778 {
2779  if ( !mPreviewEffect )
2780  {
2781  return;
2782  }
2783 
2784  mPreviewEffect->setMode( mode );
2785 }
2786 
2788 {
2789  if ( !mPreviewEffect )
2790  {
2792  }
2793 
2794  return mPreviewEffect->mode();
2795 }
2796 
2798 {
2799  if ( !mSnappingUtils )
2800  {
2801  // associate a dummy instance, but better than null pointer
2802  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
2803  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
2804  }
2805  return mSnappingUtils;
2806 }
2807 
2809 {
2810  mSnappingUtils = utils;
2811 }
2812 
2813 void QgsMapCanvas::readProject( const QDomDocument &doc )
2814 {
2815  QgsProject *project = qobject_cast< QgsProject * >( sender() );
2816 
2817  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
2818  if ( nodes.count() )
2819  {
2820  QDomNode node = nodes.item( 0 );
2821 
2822  // Search the specific MapCanvas node using the name
2823  if ( nodes.count() > 1 )
2824  {
2825  for ( int i = 0; i < nodes.size(); ++i )
2826  {
2827  QDomElement elementNode = nodes.at( i ).toElement();
2828 
2829  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
2830  {
2831  node = nodes.at( i );
2832  break;
2833  }
2834  }
2835  }
2836 
2837  QgsMapSettings tmpSettings;
2838  tmpSettings.readXml( node );
2839  if ( objectName() != QLatin1String( "theMapCanvas" ) )
2840  {
2841  // never manually set the crs for the main canvas - this is instead connected to the project CRS
2842  setDestinationCrs( tmpSettings.destinationCrs() );
2843  }
2844  setExtent( tmpSettings.extent() );
2845  setRotation( tmpSettings.rotation() );
2847 
2848  clearExtentHistory(); // clear the extent history on project load
2849 
2850  QDomElement elem = node.toElement();
2851  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
2852  {
2853  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
2854  {
2855  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
2856  }
2857  }
2858  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
2859 
2860  // restore canvas expression context
2861  const QDomNodeList scopeElements = elem.elementsByTagName( QStringLiteral( "expressionContextScope" ) );
2862  if ( scopeElements.size() > 0 )
2863  {
2864  const QDomElement scopeElement = scopeElements.at( 0 ).toElement();
2865  mExpressionContextScope.readXml( scopeElement, QgsReadWriteContext() );
2866  }
2867  }
2868  else
2869  {
2870  QgsDebugMsg( QStringLiteral( "Couldn't read mapcanvas information from project" ) );
2872  {
2874  clearExtentHistory(); // clear the extent history on project load
2875  }
2876  }
2877 }
2878 
2879 void QgsMapCanvas::writeProject( QDomDocument &doc )
2880 {
2881  // create node "mapcanvas" and call mMapRenderer->writeXml()
2882 
2883  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
2884  if ( !nl.count() )
2885  {
2886  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
2887  return;
2888  }
2889  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
2890 
2891  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
2892  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
2893  if ( !mTheme.isEmpty() )
2894  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
2895  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
2896  qgisNode.appendChild( mapcanvasNode );
2897 
2898  mSettings.writeXml( mapcanvasNode, doc );
2899 
2900  // store canvas expression context
2901  QDomElement scopeElement = doc.createElement( QStringLiteral( "expressionContextScope" ) );
2902  QgsExpressionContextScope tmpScope( mExpressionContextScope );
2903  tmpScope.removeVariable( QStringLiteral( "atlas_featurenumber" ) );
2904  tmpScope.removeVariable( QStringLiteral( "atlas_pagename" ) );
2905  tmpScope.removeVariable( QStringLiteral( "atlas_feature" ) );
2906  tmpScope.removeVariable( QStringLiteral( "atlas_featureid" ) );
2907  tmpScope.removeVariable( QStringLiteral( "atlas_geometry" ) );
2908  tmpScope.writeXml( scopeElement, doc, QgsReadWriteContext() );
2909  mapcanvasNode.appendChild( scopeElement );
2910 
2911  // TODO: store only units, extent, projections, dest CRS
2912 }
2913 
2914 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center, bool ignoreScaleLock )
2915 {
2916  if ( mScaleLocked && !ignoreScaleLock )
2917  {
2918  ScaleRestorer restorer( this );
2920  }
2921  else
2922  {
2924  r.scale( scaleFactor, center );
2925  setExtent( r, true );
2926  refresh();
2927  }
2928 }
2929 
2931 {
2932  // Find out which layer it was that sent the signal.
2933  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( sender() );
2934  if ( layer )
2935  {
2936  emit selectionChanged( layer );
2937  refresh();
2938  }
2939 }
2940 
2941 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *event )
2942 {
2943  // By default graphics view delegates the drag events to graphics items.
2944  // But we do not want that and by ignoring the drag enter we let the
2945  // parent (e.g. QgisApp) to handle drops of map layers etc.
2946 
2947  // so we ONLY accept the event if we know in advance that a custom drop handler
2948  // wants it
2949 
2950  if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
2951  {
2953  bool allHandled = true;
2954  for ( const QgsMimeDataUtils::Uri &uri : lst )
2955  {
2956  bool handled = false;
2957  for ( QgsCustomDropHandler *handler : std::as_const( mDropHandlers ) )
2958  {
2959  if ( handler->canHandleCustomUriCanvasDrop( uri, this ) )
2960  {
2961  handled = true;
2962  break;
2963  }
2964  }
2965  if ( !handled )
2966  allHandled = false;
2967  }
2968  if ( allHandled )
2969  event->accept();
2970  else
2971  event->ignore();
2972  }
2973  else
2974  {
2975  event->ignore();
2976  }
2977 }
2978 
2979 bool QgsMapCanvas::viewportEvent( QEvent *event )
2980 {
2981  if ( event->type() == QEvent::ToolTip && mMapTool && mMapTool->canvasToolTipEvent( qgis::down_cast<QHelpEvent *>( event ) ) )
2982  {
2983  return true;
2984  }
2985  return QGraphicsView::viewportEvent( event );
2986 }
2987 
2988 void QgsMapCanvas::mapToolDestroyed()
2989 {
2990  QgsDebugMsgLevel( QStringLiteral( "maptool destroyed" ), 2 );
2991  mMapTool = nullptr;
2992 }
2993 
2994 bool QgsMapCanvas::event( QEvent *e )
2995 {
2996  if ( !QTouchDevice::devices().empty() )
2997  {
2998  if ( e->type() == QEvent::Gesture )
2999  {
3000  if ( QTapAndHoldGesture *tapAndHoldGesture = qobject_cast< QTapAndHoldGesture * >( static_cast<QGestureEvent *>( e )->gesture( Qt::TapAndHoldGesture ) ) )
3001  {
3002  QPointF pos = tapAndHoldGesture->position();
3003  pos = mapFromGlobal( QPoint( pos.x(), pos.y() ) );
3004  QgsPointXY mapPoint = getCoordinateTransform()->toMapCoordinates( pos.x(), pos.y() );
3005  emit tapAndHoldGestureOccurred( mapPoint, tapAndHoldGesture );
3006  }
3007 
3008  // call handler of current map tool
3009  if ( mMapTool )
3010  {
3011  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
3012  }
3013  }
3014  }
3015 
3016  // pass other events to base class
3017  return QGraphicsView::event( e );
3018 }
3019 
3021 {
3022  // reload all layers in canvas
3023  const QList<QgsMapLayer *> layers = mapSettings().layers();
3024  for ( QgsMapLayer *layer : layers )
3025  {
3026  layer->reload();
3027  }
3028 
3029  redrawAllLayers();
3030 }
3031 
3033 {
3034  // clear the cache
3035  clearCache();
3036 
3037  // and then refresh
3038  refresh();
3039 }
3040 
3042 {
3043  while ( mRefreshScheduled || mJob )
3044  {
3045  QgsApplication::processEvents();
3046  }
3047 }
3048 
3050 {
3051  mSettings.setSegmentationTolerance( tolerance );
3052 }
3053 
3055 {
3056  mSettings.setSegmentationToleranceType( type );
3057 }
3058 
3059 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
3060 {
3061  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
3062  const QList<QGraphicsItem *> items = mScene->items();
3063  for ( QGraphicsItem *gi : items )
3064  {
3065  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( gi );
3066  if ( aItem )
3067  {
3068  annotationItemList.push_back( aItem );
3069  }
3070  }
3071 
3072  return annotationItemList;
3073 }
3074 
3076 {
3077  mAnnotationsVisible = show;
3078  const QList<QgsMapCanvasAnnotationItem *> items = annotationItems();
3079  for ( QgsMapCanvasAnnotationItem *item : items )
3080  {
3081  item->setVisible( show );
3082  }
3083 }
3084 
3086 {
3087  mSettings.setLabelingEngineSettings( settings );
3088 }
3089 
3091 {
3092  return mSettings.labelingEngineSettings();
3093 }
3094 
3095 void QgsMapCanvas::startPreviewJobs()
3096 {
3097  stopPreviewJobs(); //just in case still running
3098 
3099  //canvas preview jobs aren't compatible with rotation
3100  // TODO fix this
3101  if ( !qgsDoubleNear( mSettings.rotation(), 0.0 ) )
3102  return;
3103 
3104  schedulePreviewJob( 0 );
3105 }
3106 
3107 void QgsMapCanvas::startPreviewJob( int number )
3108 {
3109  QgsRectangle mapRect = mSettings.visibleExtent();
3110 
3111  if ( number == 4 )
3112  number += 1;
3113 
3114  int j = number / 3;
3115  int i = number % 3;
3116 
3117  //copy settings, only update extent
3118  QgsMapSettings jobSettings = mSettings;
3119 
3120  double dx = ( i - 1 ) * mapRect.width();
3121  double dy = ( 1 - j ) * mapRect.height();
3122  QgsRectangle jobExtent = mapRect;
3123 
3124  jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
3125  jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
3126  jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
3127  jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
3128 
3129  jobSettings.setExtent( jobExtent );
3130  jobSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, false );
3131  jobSettings.setFlag( Qgis::MapSettingsFlag::RenderPreviewJob, true );
3132 
3133  // truncate preview layers to fast layers
3134  const QList<QgsMapLayer *> layers = jobSettings.layers();
3135  QList< QgsMapLayer * > previewLayers;
3137  context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
3138  for ( QgsMapLayer *layer : layers )
3139  {
3140  context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
3141  QgsDataProvider *provider = layer->dataProvider();
3142  if ( provider && !provider->renderInPreview( context ) )
3143  {
3144  QgsDebugMsgLevel( QStringLiteral( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
3145  continue;
3146  }
3147 
3148  previewLayers << layer;
3149  }
3150  jobSettings.setLayers( previewLayers );
3151 
3152  QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
3153  job->setProperty( "number", number );
3154  mPreviewJobs.append( job );
3155  connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
3156  job->start();
3157 }
3158 
3159 void QgsMapCanvas::stopPreviewJobs()
3160 {
3161  mPreviewTimer.stop();
3162  const auto previewJobs = mPreviewJobs;
3163  for ( auto previewJob : previewJobs )
3164  {
3165  if ( previewJob )
3166  {
3167  disconnect( previewJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
3168  connect( previewJob, &QgsMapRendererQImageJob::finished, previewJob, &QgsMapRendererQImageJob::deleteLater );
3169  previewJob->cancelWithoutBlocking();
3170  }
3171  }
3172  mPreviewJobs.clear();
3173 }
3174 
3175 void QgsMapCanvas::schedulePreviewJob( int number )
3176 {
3177  mPreviewTimer.setSingleShot( true );
3178  mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
3179  disconnect( mPreviewTimerConnection );
3180  mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
3181  {
3182  startPreviewJob( number );
3183  } );
3184  mPreviewTimer.start();
3185 }
3186 
3187 bool QgsMapCanvas::panOperationInProgress()
3188 {
3189  if ( mCanvasProperties->panSelectorDown )
3190  return true;
3191 
3192  if ( QgsMapToolPan *panTool = qobject_cast< QgsMapToolPan *>( mMapTool ) )
3193  {
3194  if ( panTool->isDragging() )
3195  return true;
3196  }
3197 
3198  return false;
3199 }
3200 
3201 int QgsMapCanvas::nextZoomLevel( const QList<double> &resolutions, bool zoomIn ) const
3202 {
3203  int resolutionLevel = -1;
3204  double currentResolution = mapUnitsPerPixel();
3205 
3206  for ( int i = 0, n = resolutions.size(); i < n; ++i )
3207  {
3208  if ( qgsDoubleNear( resolutions[i], currentResolution, 0.0001 ) )
3209  {
3210  resolutionLevel = zoomIn ? ( i - 1 ) : ( i + 1 );
3211  break;
3212  }
3213  else if ( currentResolution <= resolutions[i] )
3214  {
3215  resolutionLevel = zoomIn ? ( i - 1 ) : i;
3216  break;
3217  }
3218  }
3219  return ( resolutionLevel < 0 || resolutionLevel >= resolutions.size() ) ? -1 : resolutionLevel;
3220 }
3221 
3223 {
3224  if ( !mZoomResolutions.isEmpty() )
3225  {
3226  int zoomLevel = nextZoomLevel( mZoomResolutions, true );
3227  if ( zoomLevel != -1 )
3228  {
3229  return mZoomResolutions.at( zoomLevel ) / mapUnitsPerPixel();
3230  }
3231  }
3232  return 1 / mWheelZoomFactor;
3233 }
3234 
3236 {
3237  if ( !mZoomResolutions.isEmpty() )
3238  {
3239  int zoomLevel = nextZoomLevel( mZoomResolutions, false );
3240  if ( zoomLevel != -1 )
3241  {
3242  return mZoomResolutions.at( zoomLevel ) / mapUnitsPerPixel();
3243  }
3244  }
3245  return mWheelZoomFactor;
3246 }
@ View
Renderer used for displaying on screen.
@ DrawEditingInfo
Enable drawing of vertex markers for layers in editing mode.
@ RenderPreviewJob
Render is a 'canvas preview' render, and shortcuts should be taken to ensure fast rendering.
@ RenderMapTile
Draw map such that there are no problems between adjacent tiles.
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
@ RenderPartialOutput
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
@ Antialiasing
Enable anti-aliasing for map rendering.
@ DrawLabeling
Enable drawing of labels on top of the map.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
@ MaximumAngle
Maximum angle between generating radii (lines from arc center to output vertices)
virtual bool isEmpty() const
Returns true if the geometry is empty.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
void userCrsChanged(const QString &id)
Emitted whenever an existing user CRS definition is changed.
This class represents a coordinate reference system (CRS).
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Abstract base class that may be implemented to handle new types of data to be dropped in QGIS.
Abstract base class for spatial data provider implementations.
virtual bool renderInPreview(const QgsDataProvider::PreviewContext &context)
Returns whether the layer must be rendered in preview jobs.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const SIP_THROW(QgsCsException)
Computes the bearing (in radians) between two points.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QgsUnitTypes::DistanceUnit lengthUnits() const
Returns the units of distance for length calculations made by this object.
QgsRange which stores a range of double values.
Definition: qgsrange.h:203
QString what() const
Definition: qgsexception.h:48
Abstract interface for generating an expression context scope.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads scope variables from an XML element.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes scope variables to an XML element.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
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.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:223
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
Creates a new geometry from a QgsPointXY object.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
Stores global configuration for labeling engine.
Class that stores computed placement from labeling engine.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
An interactive map canvas item which displays a QgsAnnotation.
An interface for objects which block interactions with a QgsMapCanvas.
Interaction
Available interactions to block.
An abstract class for items that can be placed on the map canvas.
virtual void updatePosition()
called on changed extent or resize event to update position of the item
Snapping utils instance that is connected to a canvas and updates the configuration (map settings + c...
Deprecated to be deleted, stuff from here should be moved elsewhere.
QPoint mouseLastXY
Last seen point of the mouse.
bool panSelectorDown
Flag to indicate the pan selector key is held down by user.
CanvasProperties()=default
Constructor for CanvasProperties.
QPoint rubberStartPoint
Beginning point of a rubber band.
bool mouseButtonDown
Flag to indicate status of mouse button.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
void setCurrentLayer(QgsMapLayer *layer)
void setCustomDropHandlers(const QVector< QPointer< QgsCustomDropHandler >> &handlers)
Sets a list of custom drop handlers to use when drop events occur on the canvas.
void messageEmitted(const QString &title, const QString &message, Qgis::MessageLevel=Qgis::MessageLevel::Info)
emit a message (usually to be displayed in a message bar)
void contextMenuAboutToShow(QMenu *menu, QgsMapMouseEvent *event)
Emitted before the map canvas context menu will be shown.
void zoomToProjectExtent()
Zoom to the full extent the project associated with this canvas.
QgsUnitTypes::DistanceUnit mapUnits() const
Convenience function for returning the current canvas map units.
void freeze(bool frozen=true)
Freeze/thaw the map canvas.
void enableAntiAliasing(bool flag)
used to determine if anti-aliasing is enabled or not
void setSnappingUtils(QgsSnappingUtils *utils)
Assign an instance of snapping utils to the map canvas.
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
void zoomToFullExtent()
Zoom to the full extent of all layers currently visible in the canvas.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers that should be shown in the canvas.
void setProject(QgsProject *project)
Sets the project linked to this canvas.
const QgsRenderedItemResults * renderedItemResults(bool allowOutdatedResults=true) const
Gets access to the rendered item results (may be nullptr), which includes the results of rendering an...
QColor selectionColor() const
Returns color for selected features.
bool event(QEvent *e) override
~QgsMapCanvas() override
void setCachingEnabled(bool enabled)
Set whether to cache images of rendered layers.
void mouseReleaseEvent(QMouseEvent *e) override
QgsDoubleRange zRange() const
Returns the range of z-values which will be visible in the map.
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas to the specified rectangle.
void setRenderFlag(bool flag)
Sets whether a user has disabled canvas renders via the GUI.
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
void updateCanvasItemPositions()
called on resize or changed extent to notify canvas items to change their rectangle
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void extentsChanged()
Emitted when the extents of the map change.
QgsExpressionContextScope * defaultExpressionContextScope() const
Creates a new scope which contains default variables and functions relating to the map canvas.
void xyCoordinates(const QgsPointXY &p)
Emits current mouse position.
void stopRendering()
stop rendering (if there is any right now)
bool previewJobsEnabled
Definition: qgsmapcanvas.h:103
void magnificationChanged(double)
Emitted when the scale of the map changes.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets global labeling engine settings in the internal map settings.
QgsPointXY center() const
Gets map center, in geographical coordinates.
void showEvent(QShowEvent *event) override
int layerCount() const
Returns number of layers on the map.
bool antiAliasingEnabled() const
true if antialiasing is enabled
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets a preview mode for the map canvas.
void layerStateChange()
This slot is connected to the visibility change of one or more layers.
QgsPreviewEffect::PreviewMode previewMode() const
Returns the current preview mode for the map canvas.
void zoomScale(double scale, bool ignoreScaleLock=false)
Zooms the canvas to a specific scale.
void zoomWithCenter(int x, int y, bool zoomIn)
Zooms in/out with a given center.
QPoint mouseLastXY()
returns last position of mouse cursor
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled)
void renderComplete(QPainter *)
Emitted when the canvas has rendered.
void tapAndHoldGestureOccurred(const QgsPointXY &mapPoint, QTapAndHoldGesture *gesture)
Emitted whenever a tap and hold gesture occurs at the specified map point.
void zoomNextStatusChanged(bool)
Emitted when zoom next status changed.
const QgsDateTimeRange & temporalRange() const
Returns map canvas datetime range.
void setCanvasColor(const QColor &_newVal)
Write property of QColor bgColor.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr, bool ignoreScaleLock=false)
Zoom with the factor supplied.
const QgsTemporalController * temporalController() const
Gets access to the temporal controller that will be used to update the canvas temporal range.
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.
void setMapUpdateInterval(int timeMilliseconds)
Set how often map preview should be updated while it is being rendered (in milliseconds)
void rotationChanged(double)
Emitted when the rotation of the map changes.
void selectionChanged(QgsVectorLayer *layer)
Emitted when selection in any layer gets changed.
void dragEnterEvent(QDragEnterEvent *e) override
bool isDrawing()
Find out whether rendering is in progress.
void zRangeChanged()
Emitted when the map canvas z (elevation) range changes.
void keyPressEvent(QKeyEvent *e) override
void setZRange(const QgsDoubleRange &range)
Sets the range of z-values which will be visible in the map.
void clearExtentHistory()
Clears the list of extents and sets current extent as first item.
void zoomToPreviousExtent()
Zoom to the previous extent (view)
void enableMapTileRendering(bool flag)
sets map tile rendering flag
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers shown within the map canvas.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns global labeling engine settings from the internal map settings.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
void canvasColorChanged()
Emitted when canvas background color changes.
double zoomInFactor() const
Returns the zoom in factor.
void panToSelected(QgsVectorLayer *layer=nullptr)
Pan to the selected features of current (vector) layer keeping same extent.
void saveAsImage(const QString &fileName, QPixmap *QPixmap=nullptr, const QString &="PNG")
Save the contents of the map canvas to disk as an image.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
void setTemporalRange(const QgsDateTimeRange &range)
Set datetime range for the map canvas.
void moveCanvasContents(bool reset=false)
called when panning is in action, reset indicates end of panning
void zoomOut()
Zoom out with fixed factor.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
void setTemporalController(QgsTemporalController *controller)
Sets the temporal controller for this canvas.
void renderErrorOccurred(const QString &error, QgsMapLayer *layer)
Emitted whenever an error is encountered during a map render operation.
void waitWhileRendering()
Blocks until the rendering job has finished.
void mapRefreshCanceled()
Emitted when the pending map refresh has been canceled.
double magnificationFactor() const
Returns the magnification factor.
void writeProject(QDomDocument &)
called to write map canvas settings to project
void mousePressEvent(QMouseEvent *e) override
void updateScale()
Emits signal scaleChanged to update scale in main window.
void setMagnificationFactor(double factor, const QgsPointXY *center=nullptr)
Sets the factor of magnification to apply to the map canvas.
void refreshAllLayers()
Reload all layers (including refreshing layer properties from their data sources),...
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void panActionEnd(QPoint releasePoint)
Ends pan action and redraws the canvas.
void resizeEvent(QResizeEvent *e) override
double zoomOutFactor() const
Returns the zoom in factor.
void renderStarting()
Emitted when the canvas is about to be rendered.
void setMapSettingsFlags(Qgis::MapSettingsFlags flags)
Resets the flags for the canvas' map settings.
std::unique_ptr< CanvasProperties > mCanvasProperties
Handle pattern for implementation object.
void keyReleased(QKeyEvent *e)
Emit key release event.
void setWheelFactor(double factor)
Sets wheel zoom factor (should be greater than 1)
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
void layerStyleOverridesChanged()
Emitted when the configuration of overridden layer styles changes.
QgsMapCanvas(QWidget *parent=nullptr)
Constructor.
void panActionStart(QPoint releasePoint)
Starts a pan action.
void setPreviewJobsEnabled(bool enabled)
Sets whether canvas map preview jobs (low priority render jobs which render portions of the view just...
bool setReferencedExtent(const QgsReferencedRectangle &extent) SIP_THROW(QgsCsException)
Sets the canvas to the specified extent.
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
void redrawAllLayers()
Clears all cached images and redraws all layers.
void keyReleaseEvent(QKeyEvent *e) override
bool isFrozen() const
Returns true if canvas is frozen.
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter=true)
Centers canvas extent to feature ids.
void scaleChanged(double)
Emitted when the scale of the map changes.
void scaleLockChanged(bool locked)
Emitted when the scale locked state of the map changes.
const QgsLabelingResults * labelingResults(bool allowOutdatedResults=true) const
Gets access to the labeling results (may be nullptr).
void mouseMoveEvent(QMouseEvent *e) override
QgsRectangle projectExtent() const
Returns the associated project's full extent, in the canvas' CRS.
void setCenter(const QgsPointXY &center)
Set the center of the map canvas, in geographical coordinates.
void setParallelRenderingEnabled(bool enabled)
Set whether the layers are rendered in parallel or sequentially.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets destination coordinate reference system.
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 installInteractionBlocker(QgsMapCanvasInteractionBlocker *blocker)
Installs an interaction blocker onto the canvas, which may prevent certain map canvas interactions fr...
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
void panDistanceBearingChanged(double distance, QgsUnitTypes::DistanceUnit unit, double bearing)
Emitted whenever the distance or bearing of an in-progress panning operation is changed.
double scale() const
Returns the last reported scale of the canvas.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
double rotation() const
Gets the current map canvas rotation in clockwise degrees.
void temporalRangeChanged()
Emitted when the map canvas temporal range changes.
void paintEvent(QPaintEvent *e) override
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
void themeChanged(const QString &theme)
Emitted when the canvas has been assigned a different map theme.
void destinationCrsChanged()
Emitted when map CRS has changed.
void transformContextChanged()
Emitted when the canvas transform context is changed.
QgsMapTool * mapTool()
Returns the currently active tool.
void keyPressed(QKeyEvent *e)
Emit key press event.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
QColor canvasColor() const
Read property of QColor bgColor.
void mapCanvasRefreshed()
Emitted when canvas finished a refresh request.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds)
void zoomLastStatusChanged(bool)
Emitted when zoom last status changed.
void setSelectionColor(const QColor &color)
Set color of selected vector features.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
void mouseDoubleClickEvent(QMouseEvent *e) override
void selectionChangedSlot()
Receives signal about selection change, and pass it on with layer info.
bool viewportEvent(QEvent *event) override
void zoomToNextExtent()
Zoom to the next extent (view)
void layersChanged()
Emitted when a new set of layers has been received.
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
void zoomIn()
Zoom in with fixed factor.
QgsMapLayer * layer(int index)
Returns the map layer at position index in the layer stack.
void cancelJobs()
Cancel any rendering job, in a blocking way.
bool allowInteraction(QgsMapCanvasInteractionBlocker::Interaction interaction) const
Returns true if the specified interaction is currently permitted on the canvas.
void wheelEvent(QWheelEvent *e) override
bool previewModeEnabled() const
Returns whether a preview mode is enabled for the map canvas.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
void dropEvent(QDropEvent *event) override
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
QgsProject * project()
Returns the project linked to this canvas.
QString theme
Definition: qgsmapcanvas.h:102
void setScaleLocked(bool isLocked)
Lock the scale, so zooming can be performed using magnication.
void setRotation(double degrees)
Set the rotation of the map canvas in clockwise degrees.
void zoomToSelected(QgsVectorLayer *layer=nullptr)
Zoom to the extent of the selected features of provided (vector) layer.
void removeInteractionBlocker(QgsMapCanvasInteractionBlocker *blocker)
Removes an interaction blocker from the canvas.
void readProject(const QDomDocument &)
called to read map canvas settings from project
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
void zoomToFeatureExtent(QgsRectangle &rect)
Zooms to feature extent.
QMap< QString, QString > layerStyleOverrides() const
Returns the stored overrides of styles for layers.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void refresh()
Repaints the canvas map.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
virtual QgsMapLayerElevationProperties::Flags flags() const
Returns flags associated to the elevation properties.
@ FlagDontInvalidateCachedRendersWhenRangeChanges
Any cached rendering will not be invalidated when z range context is modified.
virtual bool hasElevation() const
Returns true if the layer has an elevation or z component.
static QgsRectangle combinedExtent(const QList< QgsMapLayer * > &layers, const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &transformContext)
Returns the combined extent of a list of layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
Definition: qgsmaplayer.h:1496
int autoRefreshInterval
Definition: qgsmaplayer.h:77
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
virtual Q_INVOKABLE void reload()
Synchronises with changes in the datasource.
Definition: qgsmaplayer.h:537
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1489
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
This class is responsible for keeping cache of rendered images resulting from a map rendering job.
void clear()
Invalidates the cache contents, clearing all cached images.
void invalidateCacheForLayer(QgsMapLayer *layer)
Invalidates cached images which relate to the specified map layer.
void clearCacheImage(const QString &cacheKey)
Removes an image from the cache with matching cacheKey.
Job implementation that renders everything sequentially using a custom painter.
void waitForFinished() override
Block until the job has finished.
virtual void waitForFinished()=0
Block until the job has finished.
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
virtual QgsLabelingResults * takeLabelingResults()=0
Gets pointer to internal labeling engine (in order to get access to the results).
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
virtual bool usedCachedLabels() const =0
Returns true if the render job was able to use a cached labeling solution.
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
static const QgsSettingsEntryBool settingsLogCanvasRefreshEvent
Settings entry log canvas refresh event.
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
void finished()
emitted when asynchronous rendering is finished (or canceled).
void start()
Start the rendering job and immediately return.
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
QStringList layersRedrawnFromCache() const
Returns a list of the layer IDs for all layers which were redrawn from cached images.
QList< QgsMapRendererJob::Error > Errors
virtual bool isActive() const =0
Tell whether the rendering job is currently running in background.
void setLayerRenderingTimeHints(const QHash< QString, int > &hints)
Sets approximate render times (in ms) for map layers.
QgsRenderedItemResults * takeRenderedItemResults()
Takes the rendered item results from the map render job and returns them.
virtual void cancelWithoutBlocking()=0
Triggers cancellation of the rendering job without blocking.
Job implementation that renders all layers in parallel.
Intermediate base class adding functionality that allows client to query the rendered image.
virtual QImage renderedImage()=0
Gets a preview/resulting image.
Job implementation that renders everything sequentially in one thread.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
The QgsMapSettings class contains configuration for rendering of the map.
void writeXml(QDomNode &node, QDomDocument &doc)
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
void setSelectionColor(const QColor &color)
Sets the color that is used for drawing of selected vector features.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
double scale() const
Returns the calculated map scale.
void setFlags(Qgis::MapSettingsFlags flags)
Sets combination of flags that will be used for rendering.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer's CRS to output CRS
QgsDoubleRange zRange() const
Returns the range of z-values which will be visible in the map.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
void setDpiTarget(double dpi)
Sets the target dpi (dots per inch) to be taken into consideration when rendering.
double magnificationFactor() const
Returns the magnification factor.
QStringList layerIds(bool expandGroupLayers=false) const
Returns the list of layer IDs which will be rendered in the map.
void setDevicePixelRatio(float dpr)
Sets the device pixel ratio.
void setZRange(const QgsDoubleRange &range)
Sets the range of z-values which will be visible in the map.
QColor backgroundColor() const
Returns the background color of the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
void setRendererUsage(Qgis::RendererUsage rendererUsage)
Sets the rendering usage.
float devicePixelRatio() const
Returns the device pixel ratio.
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the map of map layer style overrides (key: layer ID, value: style name) where a different style ...
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsUnitTypes::DistanceUnit mapUnits() const
Returns the units of the map's geographical coordinates - used for scale calculation.
const QgsMapToPixel & mapToPixel() const
QColor selectionColor() const
Returns the color that is used for drawing of selected vector features.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
QgsRectangle fullExtent() const
returns current extent of layer set
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
void setRotation(double rotation)
Sets the rotation of the resulting map image, in degrees clockwise.
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
bool testFlag(Qgis::MapSettingsFlag flag) const
Check whether a particular flag is enabled.
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
void readXml(QDomNode &node)
void setMagnificationFactor(double factor, const QgsPointXY *center=nullptr)
Set the magnification factor.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
void mapThemesChanged()
Emitted when map themes within the collection are changed.
void mapThemeRenamed(const QString &name, const QString &newName)
Emitted when a map theme within the collection is renamed.
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
A map tool for panning the map.
Definition: qgsmaptoolpan.h:33
Abstract base class for all map tools.
Definition: qgsmaptool.h:71
virtual void populateContextMenu(QMenu *menu)
Allows the tool to populate and customize the given menu, prior to showing it in response to a right-...
Definition: qgsmaptool.cpp:249
virtual bool canvasToolTipEvent(QHelpEvent *e)
Tooltip event for overriding.
Definition: qgsmaptool.cpp:209
virtual void canvasDoubleClickEvent(QgsMapMouseEvent *e)
Mouse double-click event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:173
virtual bool populateContextMenuWithEvent(QMenu *menu, QgsMapMouseEvent *event)
Allows the tool to populate and customize the given menu, prior to showing it in response to a right-...
Definition: qgsmaptool.cpp:255
virtual void canvasPressEvent(QgsMapMouseEvent *e)
Mouse press event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:178
virtual void canvasMoveEvent(QgsMapMouseEvent *e)
Mouse move event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:168
virtual void keyPressEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:193
virtual void keyReleaseEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:198
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:123
virtual void canvasReleaseEvent(QgsMapMouseEvent *e)
Mouse release event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:183
virtual void wheelEvent(QWheelEvent *e)
Mouse wheel event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:188
@ AllowZoomRect
Allow zooming by rectangle (by holding shift and dragging) while the tool is active.
Definition: qgsmaptool.h:114
@ EditTool
Map tool is an edit tool, which can only be used when layer is editable.
Definition: qgsmaptool.h:113
@ ShowContextMenu
Show a context menu when right-clicking with the tool (since QGIS 3.14). See populateContextMenu().
Definition: qgsmaptool.h:115
virtual void clean()
convenient method to clean members
Definition: qgsmaptool.cpp:120
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:94
virtual bool gestureEvent(QGestureEvent *e)
gesture event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:203
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:110
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static bool isUriList(const QMimeData *data)
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
A class to represent a 2D point.
Definition: qgspointxy.h:59
double sqrDist(double x, double y) const SIP_HOLDGIL
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:190
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
void setMode(PreviewMode mode)
Sets the mode for the preview effect, which controls how the effect modifies a widgets appearance.
PreviewMode mode() const
Returns the mode used for the preview effect.
QgsReferencedRectangle defaultViewExtent() const
Returns the default view extent, which should be used as the initial map extent when this project is ...
QgsReferencedRectangle fullExtent() const
Returns the full extent of the project, which represents the maximal limits of the project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:101
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:470
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:109
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
void readProject(const QDomDocument &)
Emitted when a project is being read.
void writeProject(QDomDocument &)
Emitted when the project is being written.
const QgsProjectViewSettings * viewSettings() const
Returns the project's view settings, which contains settings and properties relating to how a QgsProj...
void transformContextChanged()
Emitted when the project transformContext() is changed.
A generic dialog to prompt the user for a Coordinate Reference System.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:256
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:161
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:166
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:172
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
A QgsRectangle with associated coordinate reference system.
Stores collated details of rendered items during a map rendering operation.
void transferResults(QgsRenderedItemResults *other, const QStringList &layerIds)
Transfers all results from an other QgsRenderedItemResults object where the items have layer IDs matc...
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:52
QColor strokeColor
Definition: qgsrubberband.h:70
void setWidth(int width)
Sets the width of the line.
void setSecondaryStrokeColor(const QColor &color)
Sets a secondary stroke color for the rubberband which will be drawn under the main stroke color.
@ ICON_CIRCLE
A circle is used to highlight points (○)
QColor fillColor
Definition: qgsrubberband.h:69
void setStrokeColor(const QColor &color)
Sets the stroke color for the rubberband.
QColor secondaryStrokeColor
Definition: qgsrubberband.h:72
void setIcon(IconType icon)
Sets the icon type to highlight point geometries.
void updatePosition() override
called on changed extent or resize event to update position of the item
void addGeometry(const QgsGeometry &geometry, QgsMapLayer *layer, bool doUpdate=true)
Adds the geometry of an existing feature to a rubberband This is useful for multi feature highlightin...
void setFillColor(const QColor &color)
Sets the fill color for the rubberband.
Scoped object for saving and restoring a QPainter object's state.
Scoped object for logging of the runtime for a single operation or group of operations.
static const QgsSettingsEntryBool settingsRespectScreenDPI
Settings entry respect screen dpi.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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:253
This class has all the configuration of snapping and can return answers to snapping queries.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
A controller base class for temporal objects, contains a signal for notifying updates of the objects ...
void updateTemporalRange(const QgsDateTimeRange &range)
Signals that a temporal range has changed and needs to be updated in all connected objects.
bool isActive() const
Returns true if the temporal property is active.
virtual QgsTemporalProperty::Flags flags() const
Returns flags associated to the temporal property.
@ FlagDontInvalidateCachedRendersWhenRangeChanges
Any cached rendering will not be invalidated when temporal range context is modified.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
void setIsTemporal(bool enabled)
Sets whether the temporal range is enabled (i.e.
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:221
void release()
Releases the cursor override early (i.e.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceDegrees
Degrees, for planar geographic CRS distance measurements.
Definition: qgsunittypes.h:75
Represents a vector layer which manages a vector based data sets.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
A class to represent a vector.
Definition: qgsvector.h:30
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
constexpr double CANVAS_MAGNIFICATION_MIN
Minimum magnification level allowed in map canvases.
Definition: qgsguiutils.h:61
constexpr double CANVAS_MAGNIFICATION_MAX
Maximum magnification level allowed in map canvases.
Definition: qgsguiutils.h:69
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1517
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
Stores settings related to the context in which a preview job runs.
double maxRenderingTimeMs
Default maximum allowable render time, in ms.
double lastRenderingTimeMs
Previous rendering time for the layer, in ms.