QGIS API Documentation 3.27.0-Master (75dc696944)
qgsmapcanvas.cpp
Go to the documentation of this file.
1/***************************************************************************
2qgsmapcanvas.cpp - description
3------------------ -
4begin : Sun Jun 30 2002
5copyright : (C) 2002 by Gary E.Sherman
6email : 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"
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"
77#include "qgssvgcache.h"
78#include "qgsimagecache.h"
80#include "qgsmimedatautils.h"
88#include "qgsruntimeprofiler.h"
90#include "qgsannotationlayer.h"
93#include "qgslabelingresults.h"
94#include "qgsmaplayerutils.h"
98#include "qgssymbollayerutils.h"
99#include "qgsvectortilelayer.h"
100
106//TODO QGIS 4.0 - remove
108{
109 public:
110
114 CanvasProperties() = default;
115
117 bool mouseButtonDown{ false };
118
121
124
126 bool panSelectorDown{ false };
127};
128
129
130
132 : QGraphicsView( parent )
133 , mCanvasProperties( new CanvasProperties )
134 , mExpressionContextScope( tr( "Map Canvas" ) )
135{
136 mScene = new QGraphicsScene();
137 setScene( mScene );
138 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
139 setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
140 setMouseTracking( true );
141 setFocusPolicy( Qt::StrongFocus );
142
143 mResizeTimer = new QTimer( this );
144 mResizeTimer->setSingleShot( true );
145 connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
146
147 mRefreshTimer = new QTimer( this );
148 mRefreshTimer->setSingleShot( true );
149 connect( mRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::refreshMap );
150
151 // create map canvas item which will show the map
152 mMap = new QgsMapCanvasMap( this );
153
154 // project handling
159
160 connect( QgsProject::instance()->mainAnnotationLayer(), &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
161 connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
162 connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeRenamed, this, &QgsMapCanvas::mapThemeRenamed );
163 connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
164
165 {
166 QgsScopedRuntimeProfile profile( "Map settings initialization" );
170 mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
172 this, [ = ]
173 {
174 mSettings.setEllipsoid( QgsProject::instance()->ellipsoid() );
175 refresh();
176 } );
179 this, [ = ]
180 {
181 mSettings.setTransformContext( QgsProject::instance()->transformContext() );
183 refresh();
184 } );
185
187 {
190 if ( mSettings.destinationCrs() != crs )
191 {
192 // user crs has changed definition, refresh the map
193 setDestinationCrs( crs );
194 }
195 } );
196 }
197
198 // refresh canvas when a remote svg/image has finished downloading
201 // refresh canvas when project color scheme is changed -- if layers use project colors, they need to be redrawn
203
204 //segmentation parameters
205 QgsSettings settings;
206 double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
207 QgsAbstractGeometry::SegmentationToleranceType toleranceType = settings.enumValue( QStringLiteral( "qgis/segmentationToleranceType" ), QgsAbstractGeometry::MaximumAngle );
208 mSettings.setSegmentationTolerance( segmentationTolerance );
209 mSettings.setSegmentationToleranceType( toleranceType );
210
211 mWheelZoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
212
213 QSize s = viewport()->size();
214 mSettings.setOutputSize( s );
215
217
218 setSceneRect( 0, 0, s.width(), s.height() );
219 mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
220
221 moveCanvasContents( true );
222
223 connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsMapCanvas::mapUpdateTimeout );
224 mMapUpdateTimer.setInterval( 250 );
225
226#ifdef Q_OS_WIN
227 // Enable touch event on Windows.
228 // Qt on Windows needs to be told it can take touch events or else it ignores them.
229 grabGesture( Qt::PinchGesture );
230 grabGesture( Qt::TapAndHoldGesture );
231 viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
232#endif
233
234 mPreviewEffect = new QgsPreviewEffect( this );
235 viewport()->setGraphicsEffect( mPreviewEffect );
236
237 mZoomCursor = QgsApplication::getThemeCursor( QgsApplication::Cursor::ZoomIn );
238
239 connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
240
242
243 setInteractive( false );
244
245 // make sure we have the same default in QgsMapSettings and the scene's background brush
246 // (by default map settings has white bg color, scene background brush is black)
247 setCanvasColor( mSettings.backgroundColor() );
248
249 setTemporalRange( mSettings.temporalRange() );
250 refresh();
251}
252
253
255{
256 if ( mMapTool )
257 {
258 mMapTool->deactivate();
259 mMapTool = nullptr;
260 }
261 mLastNonZoomMapTool = nullptr;
262
263 cancelJobs();
264
265 // delete canvas items prior to deleting the canvas
266 // because they might try to update canvas when it's
267 // already being destructed, ends with segfault
268 qDeleteAll( mScene->items() );
269
270 mScene->deleteLater(); // crashes in python tests on windows
271
272 delete mCache;
273}
274
275
277{
278 // rendering job may still end up writing into canvas map item
279 // so kill it before deleting canvas items
280 if ( mJob )
281 {
282 whileBlocking( mJob )->cancel();
283 delete mJob;
284 mJob = nullptr;
285 }
286
287 for ( auto previewJob = mPreviewJobs.constBegin(); previewJob != mPreviewJobs.constEnd(); ++previewJob )
288 {
289 if ( *previewJob )
290 {
291 whileBlocking( *previewJob )->cancel();
292 delete *previewJob;
293 }
294 }
295 mPreviewJobs.clear();
296}
297
298void QgsMapCanvas::setMagnificationFactor( double factor, const QgsPointXY *center )
299{
300 // do not go higher or lower than min max magnification ratio
301 double magnifierMin = QgsGuiUtils::CANVAS_MAGNIFICATION_MIN;
302 double magnifierMax = QgsGuiUtils::CANVAS_MAGNIFICATION_MAX;
303 factor = std::clamp( factor, magnifierMin, magnifierMax );
304
305 // the magnifier widget is in integer percent
306 if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
307 {
308 mSettings.setMagnificationFactor( factor, center );
309 refresh();
310 emit magnificationChanged( factor );
311 }
312}
313
315{
316 return mSettings.magnificationFactor();
317}
318
320{
323}
324
326{
328}
329
331{
333}
334
336{
337 QList<QgsMapLayer *> layers = mapSettings().layers();
338 if ( index >= 0 && index < layers.size() )
339 return layers[index];
340 else
341 return nullptr;
342}
343
344QgsMapLayer *QgsMapCanvas::layer( const QString &id )
345{
346 // first check for layers from canvas map settings
347 const QList<QgsMapLayer *> layers = mapSettings().layers();
348 for ( QgsMapLayer *layer : layers )
349 {
350 if ( layer && layer->id() == id )
351 return layer;
352 }
353
354 // else fallback to searching project layers
355 // TODO: allow a specific project to be associated with a canvas!
356 return QgsProject::instance()->mapLayer( id );
357}
358
360{
361 if ( mCurrentLayer == layer )
362 return;
363
364 mCurrentLayer = layer;
366}
367
369{
370 return mapSettings().scale();
371}
372
374{
375 return nullptr != mJob;
376} // isDrawing
377
378// return the current coordinate transform based on the extents and
379// device size
381{
382 return &mapSettings().mapToPixel();
383}
384
385void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
386{
387 // following a theme => request denied!
388 if ( !mTheme.isEmpty() )
389 return;
390
391 setLayersPrivate( layers );
392}
393
394void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
395{
396 QList<QgsMapLayer *> oldLayers = mSettings.layers();
397
398 // update only if needed
399 if ( layers == oldLayers )
400 return;
401
402 const auto constOldLayers = oldLayers;
403 for ( QgsMapLayer *layer : constOldLayers )
404 {
405 disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
406 disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
407 switch ( layer->type() )
408 {
410 {
411 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
413 disconnect( vlayer, &QgsVectorLayer::rendererChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
414 break;
415 }
416
418 {
419 QgsVectorTileLayer *vtlayer = qobject_cast<QgsVectorTileLayer *>( layer );
421 break;
422 }
423
430 break;
431 }
432 }
433
434 mSettings.setLayers( layers );
435
436 const auto constLayers = layers;
437 for ( QgsMapLayer *layer : constLayers )
438 {
439 if ( !layer )
440 continue;
441 connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
442 connect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
443
444 switch ( layer->type() )
445 {
447 {
448 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
450 connect( vlayer, &QgsVectorLayer::rendererChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
451 break;
452 }
453
455 {
456 QgsVectorTileLayer *vtlayer = qobject_cast<QgsVectorTileLayer *>( layer );
458 break;
459 }
460
467 break;
468 }
469 }
470
471 QgsDebugMsgLevel( QStringLiteral( "Layers have changed, refreshing" ), 2 );
472 emit layersChanged();
473
474 updateAutoRefreshTimer();
475 refresh();
476}
477
478
480{
481 return mSettings;
482}
483
485{
486 if ( mSettings.destinationCrs() == crs )
487 return;
488
489 // try to reproject current extent to the new one
490 QgsRectangle rect;
491 if ( !mSettings.visibleExtent().isEmpty() )
492 {
493 const QgsCoordinateTransform transform( mSettings.destinationCrs(), crs, QgsProject::instance(),
496 try
497 {
498 rect = transform.transformBoundingBox( mSettings.visibleExtent() );
499 }
500 catch ( QgsCsException &e )
501 {
502 Q_UNUSED( e )
503 QgsDebugMsg( QStringLiteral( "Transform error caught: %1" ).arg( e.what() ) );
504 }
505 }
506
507 if ( !rect.isEmpty() )
508 {
509 // we will be manually calling updateCanvasItemPositions() later, AFTER setting the updating the mSettings destination CRS, and we don't
510 // want to do that twice!
511 mBlockItemPositionUpdates++;
512 setExtent( rect );
513 mBlockItemPositionUpdates--;
514 }
515
516 mSettings.setDestinationCrs( crs );
517 updateScale();
519
520 QgsDebugMsgLevel( QStringLiteral( "refreshing after destination CRS changed" ), 2 );
521 refresh();
522
524}
525
527{
528 if ( mController )
530 if ( QgsTemporalNavigationObject *temporalNavigationObject = qobject_cast< QgsTemporalNavigationObject * >( mController ) )
531 {
532 disconnect( temporalNavigationObject, &QgsTemporalNavigationObject::navigationModeChanged, this, &QgsMapCanvas::temporalControllerModeChanged );
533
534 // clear any existing animation settings from map settings. We don't do this on every render, as a 3rd party plugin
535 // might be in control of these!
536 mSettings.setFrameRate( -1 );
537 mSettings.setCurrentFrame( -1 );
538 }
539
540 mController = controller;
542 if ( QgsTemporalNavigationObject *temporalNavigationObject = qobject_cast< QgsTemporalNavigationObject * >( mController ) )
543 connect( temporalNavigationObject, &QgsTemporalNavigationObject::navigationModeChanged, this, &QgsMapCanvas::temporalControllerModeChanged );
544}
545
546void QgsMapCanvas::temporalControllerModeChanged()
547{
548 if ( QgsTemporalNavigationObject *temporalNavigationObject = qobject_cast< QgsTemporalNavigationObject * >( mController ) )
549 {
550 switch ( temporalNavigationObject->navigationMode() )
551 {
553 mSettings.setFrameRate( temporalNavigationObject->framesPerSecond() );
554 mSettings.setCurrentFrame( temporalNavigationObject->currentFrameNumber() );
555 break;
556
559 // clear any existing animation settings from map settings. We don't do this on every render, as a 3rd party plugin
560 // might be in control of these!
561 mSettings.setFrameRate( -1 );
562 mSettings.setCurrentFrame( -1 );
563 break;
564 }
565 }
566}
567
569{
570 return mController;
571}
572
573void QgsMapCanvas::setMapSettingsFlags( Qgis::MapSettingsFlags flags )
574{
575 mSettings.setFlags( flags );
576 clearCache();
577 refresh();
578}
579
580const QgsLabelingResults *QgsMapCanvas::labelingResults( bool allowOutdatedResults ) const
581{
582 if ( !allowOutdatedResults && mLabelingResultsOutdated )
583 return nullptr;
584
585 return mLabelingResults.get();
586}
587
588const QgsRenderedItemResults *QgsMapCanvas::renderedItemResults( bool allowOutdatedResults ) const
589{
590 if ( !allowOutdatedResults && mRenderedItemResultsOutdated )
591 return nullptr;
592
593 return mRenderedItemResults.get();
594}
595
597{
598 if ( enabled == isCachingEnabled() )
599 return;
600
601 if ( mJob && mJob->isActive() )
602 {
603 // wait for the current rendering to finish, before touching the cache
604 mJob->waitForFinished();
605 }
606
607 if ( enabled )
608 {
609 mCache = new QgsMapRendererCache;
610 }
611 else
612 {
613 delete mCache;
614 mCache = nullptr;
615 }
616 mPreviousRenderedItemResults.reset();
617}
618
620{
621 return nullptr != mCache;
622}
623
625{
626 if ( mCache )
627 mCache->clear();
628
629 if ( mPreviousRenderedItemResults )
630 mPreviousRenderedItemResults.reset();
631 if ( mRenderedItemResults )
632 mRenderedItemResults.reset();
633}
634
636{
637 mUseParallelRendering = enabled;
638}
639
641{
642 return mUseParallelRendering;
643}
644
645void QgsMapCanvas::setMapUpdateInterval( int timeMilliseconds )
646{
647 mMapUpdateTimer.setInterval( timeMilliseconds );
648}
649
651{
652 return mMapUpdateTimer.interval();
653}
654
655
657{
658 return mCurrentLayer;
659}
660
662{
663 QgsExpressionContextScope *s = new QgsExpressionContextScope( QObject::tr( "Map Canvas" ) );
664 s->setVariable( QStringLiteral( "canvas_cursor_point" ), QgsGeometry::fromPointXY( cursorPoint() ), true );
665 return s;
666}
667
669{
670 //build the expression context
671 QgsExpressionContext expressionContext;
672 expressionContext << QgsExpressionContextUtils::globalScope()
676 if ( QgsExpressionContextScopeGenerator *generator = dynamic_cast< QgsExpressionContextScopeGenerator * >( mController ) )
677 {
678 expressionContext << generator->createExpressionContextScope();
679 }
680 expressionContext << defaultExpressionContextScope()
681 << new QgsExpressionContextScope( mExpressionContextScope );
682 return expressionContext;
683}
684
686{
687 if ( !mSettings.hasValidSettings() )
688 {
689 QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh - invalid settings -> nothing to do" ), 2 );
690 return;
691 }
692
693 if ( !mRenderFlag || mFrozen )
694 {
695 QgsDebugMsgLevel( QStringLiteral( "CANVAS render flag off" ), 2 );
696 return;
697 }
698
699 if ( mRefreshScheduled )
700 {
701 QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh already scheduled" ), 2 );
702 return;
703 }
704
705 mRefreshScheduled = true;
706
707 QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh scheduling" ), 2 );
708
709 // schedule a refresh
710 mRefreshTimer->start( 1 );
711
712 mLabelingResultsOutdated = true;
713 mRenderedItemResultsOutdated = true;
714}
715
716void QgsMapCanvas::refreshMap()
717{
718 Q_ASSERT( mRefreshScheduled );
719
720 QgsDebugMsgLevel( QStringLiteral( "CANVAS refresh!" ), 3 );
721
722 stopRendering(); // if any...
723 stopPreviewJobs();
724
726
727 // if using the temporal controller in animation mode, get the frame settings from that
728 if ( QgsTemporalNavigationObject *temporalNavigationObject = dynamic_cast < QgsTemporalNavigationObject * >( mController ) )
729 {
730 switch ( temporalNavigationObject->navigationMode() )
731 {
733 mSettings.setFrameRate( temporalNavigationObject->framesPerSecond() );
734 mSettings.setCurrentFrame( temporalNavigationObject->currentFrameNumber() );
735 break;
736
739 break;
740 }
741 }
742
743 mSettings.setPathResolver( QgsProject::instance()->pathResolver() );
744
745 if ( !mTheme.isEmpty() )
746 {
747 // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
748 // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
749 // current state of the style. If we had stored the style overrides earlier (such as in
750 // mapThemeChanged slot) then this xml could be out of date...
751 // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
752 // just return the style name, we can instead set the overrides in mapThemeChanged and not here
753 mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
754 }
755
756 // render main annotation layer above all other layers
757 QgsMapSettings renderSettings = mSettings;
758 QList<QgsMapLayer *> allLayers = renderSettings.layers();
759 allLayers.insert( 0, QgsProject::instance()->mainAnnotationLayer() );
760 renderSettings.setLayers( allLayers );
761
762 // create the renderer job
763 Q_ASSERT( !mJob );
764 mJobCanceled = false;
765 if ( mUseParallelRendering )
766 mJob = new QgsMapRendererParallelJob( renderSettings );
767 else
768 mJob = new QgsMapRendererSequentialJob( renderSettings );
769
770 connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
771 mJob->setCache( mCache );
772 mJob->setLayerRenderingTimeHints( mLastLayerRenderTime );
773
774 mJob->start();
775
776 // from now on we can accept refresh requests again
777 // this must be reset only after the job has been started, because
778 // some providers (yes, it's you WCS and AMS!) during preparation
779 // do network requests and start an internal event loop, which may
780 // end up calling refresh() and would schedule another refresh,
781 // deleting the one we have just started.
782 mRefreshScheduled = false;
783
784 mMapUpdateTimer.start();
785
786 emit renderStarting();
787}
788
789void QgsMapCanvas::mapThemeChanged( const QString &theme )
790{
791 if ( theme == mTheme )
792 {
793 // set the canvas layers to match the new layers contained in the map theme
794 // NOTE: we do this when the theme layers change and not when we are refreshing the map
795 // as setLayers() sets up necessary connections to handle changes to the layers
796 setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
797 // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
798 // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
799 // current state of the style. If changes were made to the style then this xml
800 // snapshot goes out of sync...
801 // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
802 // just return the style name, we can instead set the overrides here and not in refreshMap()
803
804 clearCache();
805 refresh();
806 }
807}
808
809void QgsMapCanvas::mapThemeRenamed( const QString &theme, const QString &newTheme )
810{
811 if ( mTheme.isEmpty() || theme != mTheme )
812 {
813 return;
814 }
815
816 setTheme( newTheme );
817 refresh();
818}
819
820void QgsMapCanvas::rendererJobFinished()
821{
822 QgsDebugMsgLevel( QStringLiteral( "CANVAS finish! %1" ).arg( !mJobCanceled ), 2 );
823
824 mMapUpdateTimer.stop();
825
826 notifyRendererErrors( mJob->errors() );
827
828 if ( !mJobCanceled )
829 {
830 // take labeling results before emitting renderComplete, so labeling map tools
831 // connected to signal work with correct results
832 if ( !mJob->usedCachedLabels() )
833 {
834 mLabelingResults.reset( mJob->takeLabelingResults() );
835 }
836 mLabelingResultsOutdated = false;
837
838 std::unique_ptr< QgsRenderedItemResults > renderedItemResults( mJob->takeRenderedItemResults() );
839 // if a layer was redrawn from the cached version, we should copy any existing rendered item results from that layer
840 if ( mRenderedItemResults )
841 {
842 renderedItemResults->transferResults( mRenderedItemResults.get(), mJob->layersRedrawnFromCache() );
843 }
844 if ( mPreviousRenderedItemResults )
845 {
846 // also transfer any results from previous renders which happened before this
847 renderedItemResults->transferResults( mPreviousRenderedItemResults.get(), mJob->layersRedrawnFromCache() );
848 }
849
850 if ( mCache && !mPreviousRenderedItemResults )
851 mPreviousRenderedItemResults = std::make_unique< QgsRenderedItemResults >( mJob->mapSettings().extent() );
852
853 if ( mRenderedItemResults && mPreviousRenderedItemResults )
854 {
855 // for other layers which ARE present in the most recent rendered item results BUT were not part of this render, we
856 // store the results in a temporary store in case they are later switched back on and the layer's image is taken
857 // from the cache
858 mPreviousRenderedItemResults->transferResults( mRenderedItemResults.get() );
859 }
860 if ( mPreviousRenderedItemResults )
861 {
862 mPreviousRenderedItemResults->eraseResultsFromLayers( mJob->mapSettings().layerIds() );
863 }
864
865 mRenderedItemResults = std::move( renderedItemResults );
866 mRenderedItemResultsOutdated = false;
867
868 QImage img = mJob->renderedImage();
869
870 // emit renderComplete to get our decorations drawn
871 QPainter p( &img );
872 emit renderComplete( &p );
873
875 {
876 QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
877 QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
878 }
879
880 if ( mDrawRenderingStats )
881 {
882 int w = img.width(), h = img.height();
883 QFont fnt = p.font();
884 fnt.setBold( true );
885 p.setFont( fnt );
886 int lh = p.fontMetrics().height() * 2;
887 QRect r( 0, h - lh, w, lh );
888 p.setPen( Qt::NoPen );
889 p.setBrush( QColor( 0, 0, 0, 110 ) );
890 p.drawRect( r );
891 p.setPen( Qt::white );
892 QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? QStringLiteral( "PARALLEL" ) : QStringLiteral( "SEQUENTIAL" ) ).arg( mJob->renderingTime() );
893 p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
894 }
895
896 p.end();
897
898 mMap->setContent( img, imageRect( img, mSettings ) );
899
900 mLastLayerRenderTime.clear();
901 const auto times = mJob->perLayerRenderingTime();
902 for ( auto it = times.constBegin(); it != times.constEnd(); ++it )
903 {
904 mLastLayerRenderTime.insert( it.key()->id(), it.value() );
905 }
906 if ( mUsePreviewJobs && !mRefreshAfterJob )
907 startPreviewJobs();
908 }
909 else
910 {
911 mRefreshAfterJob = false;
912 }
913
914 // now we are in a slot called from mJob - do not delete it immediately
915 // so the class is still valid when the execution returns to the class
916 mJob->deleteLater();
917 mJob = nullptr;
918
919 emit mapCanvasRefreshed();
920
921 if ( mRefreshAfterJob )
922 {
923 mRefreshAfterJob = false;
924 clearTemporalCache();
925 clearElevationCache();
926 refresh();
927 }
928}
929
930void QgsMapCanvas::previewJobFinished()
931{
932 QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
933 Q_ASSERT( job );
934
935 if ( mMap )
936 {
937 mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
938 mPreviewJobs.removeAll( job );
939
940 int number = job->property( "number" ).toInt();
941 if ( number < 8 )
942 {
943 startPreviewJob( number + 1 );
944 }
945
946 delete job;
947 }
948}
949
950QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
951{
952 // This is a hack to pass QgsMapCanvasItem::setRect what it
953 // expects (encoding of position and size of the item)
954 const QgsMapToPixel &m2p = mapSettings.mapToPixel();
955 QgsPointXY topLeft = m2p.toMapCoordinates( 0, 0 );
956#ifdef QGISDEBUG
957 // do not assert this, since it might lead to crashes when changing screen while rendering
958 if ( img.devicePixelRatio() != mapSettings.devicePixelRatio() )
959 {
960 QgsLogger::warning( QStringLiteral( "The renderer map has a wrong device pixel ratio" ) );
961 }
962#endif
963 double res = m2p.mapUnitsPerPixel() / img.devicePixelRatioF();
964 QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
965 return rect;
966}
967
969{
970 return mUsePreviewJobs;
971}
972
974{
975 mUsePreviewJobs = enabled;
976}
977
978void QgsMapCanvas::setCustomDropHandlers( const QVector<QPointer<QgsCustomDropHandler> > &handlers )
979{
980 mDropHandlers = handlers;
981}
982
983void QgsMapCanvas::clearTemporalCache()
984{
985 if ( mCache )
986 {
987 bool invalidateLabels = false;
988 const QList<QgsMapLayer *> layerList = mapSettings().layers();
989 for ( QgsMapLayer *layer : layerList )
990 {
991 bool alreadyInvalidatedThisLayer = false;
992 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
993 {
994 if ( vl->renderer() && QgsSymbolLayerUtils::rendererFrameRate( vl->renderer() ) > -1 )
995 {
996 // layer has an animated symbol assigned, so we have to redraw it regardless of whether
997 // or not it has temporal settings
999 alreadyInvalidatedThisLayer = true;
1000 // we can't shortcut and "continue" here, as we still need to check whether the layer
1001 // will cause label invalidation using the logic below
1002 }
1003 }
1004
1006 {
1007 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
1008 {
1009 if ( vl->labelsEnabled() || vl->diagramsEnabled() )
1010 invalidateLabels = true;
1011 }
1012
1014 continue;
1015
1016 if ( !alreadyInvalidatedThisLayer )
1017 mCache->invalidateCacheForLayer( layer );
1018 }
1019 }
1020
1021 if ( invalidateLabels )
1022 {
1023 mCache->clearCacheImage( QStringLiteral( "_labels_" ) );
1024 mCache->clearCacheImage( QStringLiteral( "_preview_labels_" ) );
1025 }
1026 }
1027}
1028
1029void QgsMapCanvas::clearElevationCache()
1030{
1031 if ( mCache )
1032 {
1033 bool invalidateLabels = false;
1034 const QList<QgsMapLayer *> layerList = mapSettings().layers();
1035 for ( QgsMapLayer *layer : layerList )
1036 {
1038 {
1039 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
1040 {
1041 if ( vl->labelsEnabled() || vl->diagramsEnabled() )
1042 invalidateLabels = true;
1043 }
1044
1046 continue;
1047
1048 mCache->invalidateCacheForLayer( layer );
1049 }
1050 }
1051
1052 if ( invalidateLabels )
1053 {
1054 mCache->clearCacheImage( QStringLiteral( "_labels_" ) );
1055 mCache->clearCacheImage( QStringLiteral( "_preview_labels_" ) );
1056 }
1057 }
1058}
1059
1060void QgsMapCanvas::showContextMenu( QgsMapMouseEvent *event )
1061{
1062 const QgsPointXY mapPoint = event->originalMapPoint();
1063
1064 QMenu menu;
1065
1066 QMenu *copyCoordinateMenu = new QMenu( tr( "Copy Coordinate" ), &menu );
1067 copyCoordinateMenu->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );
1068
1069 auto addCoordinateFormat = [ &, this]( const QString identifier, const QgsCoordinateReferenceSystem & crs )
1070 {
1071 const QgsCoordinateTransform ct( mSettings.destinationCrs(), crs, mSettings.transformContext() );
1072 try
1073 {
1074 const QgsPointXY transformedPoint = ct.transform( mapPoint );
1075
1076 // calculate precision based on visible map extent -- if user is zoomed in, we get better precision!
1077 int displayPrecision = 0;
1078 try
1079 {
1080 QgsCoordinateTransform extentTransform = ct;
1081 extentTransform.setBallparkTransformsAreAppropriate( true );
1082 QgsRectangle extentReproj = extentTransform.transformBoundingBox( extent() );
1083 const double mapUnitsPerPixel = ( extentReproj.width() / width() + extentReproj.height() / height() ) * 0.5;
1084 if ( mapUnitsPerPixel > 10 )
1085 displayPrecision = 0;
1086 else if ( mapUnitsPerPixel > 1 )
1087 displayPrecision = 1;
1088 else if ( mapUnitsPerPixel > 0.1 )
1089 displayPrecision = 2;
1090 else if ( mapUnitsPerPixel > 0.01 )
1091 displayPrecision = 3;
1092 else if ( mapUnitsPerPixel > 0.001 )
1093 displayPrecision = 4;
1094 else if ( mapUnitsPerPixel > 0.0001 )
1095 displayPrecision = 5;
1096 else if ( mapUnitsPerPixel > 0.00001 )
1097 displayPrecision = 6;
1098 else if ( mapUnitsPerPixel > 0.000001 )
1099 displayPrecision = 7;
1100 else if ( mapUnitsPerPixel > 0.0000001 )
1101 displayPrecision = 8;
1102 else
1103 displayPrecision = 9;
1104 }
1105 catch ( QgsCsException & )
1106 {
1107 displayPrecision = crs.mapUnits() == QgsUnitTypes::DistanceDegrees ? 5 : 3;
1108 }
1109
1110 const QList< Qgis::CrsAxisDirection > axisList = crs.axisOrdering();
1111 QString firstSuffix;
1112 QString secondSuffix;
1113 if ( axisList.size() >= 2 )
1114 {
1117 }
1118
1119 QString firstNumber;
1120 QString secondNumber;
1122 {
1123 firstNumber = QString::number( transformedPoint.y(), 'f', displayPrecision );
1124 secondNumber = QString::number( transformedPoint.x(), 'f', displayPrecision );
1125 }
1126 else
1127 {
1128 firstNumber = QString::number( transformedPoint.x(), 'f', displayPrecision );
1129 secondNumber = QString::number( transformedPoint.y(), 'f', displayPrecision );
1130 }
1131
1132 QAction *copyCoordinateAction = new QAction( QStringLiteral( "%5 (%1%2, %3%4)" ).arg(
1133 firstNumber, firstSuffix, secondNumber, secondSuffix, identifier ), &menu );
1134
1135 connect( copyCoordinateAction, &QAction::triggered, this, [firstNumber, secondNumber, transformedPoint]
1136 {
1137 QClipboard *clipboard = QApplication::clipboard();
1138
1139 const QString coordinates = firstNumber + ',' + secondNumber;
1140
1141 //if we are on x11 system put text into selection ready for middle button pasting
1142 if ( clipboard->supportsSelection() )
1143 {
1144 clipboard->setText( coordinates, QClipboard::Selection );
1145 }
1146 clipboard->setText( coordinates, QClipboard::Clipboard );
1147
1148 } );
1149 copyCoordinateMenu->addAction( copyCoordinateAction );
1150 }
1151 catch ( QgsCsException & )
1152 {
1153
1154 }
1155 };
1156
1157 addCoordinateFormat( tr( "Map CRS — %1" ).arg( mSettings.destinationCrs().userFriendlyIdentifier( QgsCoordinateReferenceSystem::MediumString ) ), mSettings.destinationCrs() );
1158 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
1159 if ( mSettings.destinationCrs() != wgs84 )
1160 addCoordinateFormat( wgs84.userFriendlyIdentifier( QgsCoordinateReferenceSystem::MediumString ), wgs84 );
1161
1162 QgsSettings settings;
1163 const QString customCrsString = settings.value( QStringLiteral( "qgis/custom_coordinate_crs" ) ).toString();
1164 if ( !customCrsString.isEmpty() )
1165 {
1166 QgsCoordinateReferenceSystem customCrs( customCrsString );
1167 if ( customCrs != mSettings.destinationCrs() && customCrs != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
1168 {
1169 addCoordinateFormat( customCrs.userFriendlyIdentifier( QgsCoordinateReferenceSystem::MediumString ), customCrs );
1170 }
1171 }
1172 copyCoordinateMenu->addSeparator();
1173 QAction *setCustomCrsAction = new QAction( tr( "Set Custom CRS…" ), &menu );
1174 connect( setCustomCrsAction, &QAction::triggered, this, [ = ]
1175 {
1176 QgsProjectionSelectionDialog selector( this );
1177 selector.setCrs( QgsCoordinateReferenceSystem( customCrsString ) );
1178 if ( selector.exec() )
1179 {
1180 QgsSettings().setValue( QStringLiteral( "qgis/custom_coordinate_crs" ), selector.crs().authid().isEmpty() ? selector.crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : selector.crs().authid() );
1181 }
1182 } );
1183 copyCoordinateMenu->addAction( setCustomCrsAction );
1184
1185 menu.addMenu( copyCoordinateMenu );
1186
1187 if ( mMapTool )
1188 if ( !mapTool()->populateContextMenuWithEvent( &menu, event ) )
1189 mMapTool->populateContextMenu( &menu );
1190
1191 emit contextMenuAboutToShow( &menu, event );
1192
1193 menu.exec( event->globalPos() );
1194}
1195
1196void QgsMapCanvas::notifyRendererErrors( const QgsMapRendererJob::Errors &errors )
1197{
1198 const QDateTime currentTime = QDateTime::currentDateTime();
1199
1200 // remove errors too old
1201 for ( const QgsMapRendererJob::Error &error : errors )
1202 {
1203 const QString errorKey = error.layerID + ':' + error.message;
1204 if ( mRendererErrors.contains( errorKey ) )
1205 {
1206 const QDateTime sameErrorTime = mRendererErrors.value( errorKey );
1207
1208 if ( sameErrorTime.secsTo( currentTime ) < 60 )
1209 continue;
1210 }
1211
1212 mRendererErrors[errorKey] = currentTime;
1213
1214 if ( QgsMapLayer *layer = QgsProject::instance()->mapLayer( error.layerID ) )
1215 emit renderErrorOccurred( error.message, layer );
1216 }
1217}
1218
1219void QgsMapCanvas::updateDevicePixelFromScreen()
1220{
1221 mSettings.setDevicePixelRatio( devicePixelRatio() );
1222 // TODO: QGIS 4 -> always respect screen dpi
1224 {
1225 if ( window()->windowHandle() )
1226 {
1227 mSettings.setOutputDpi( window()->windowHandle()->screen()->physicalDotsPerInch() );
1228 mSettings.setDpiTarget( window()->windowHandle()->screen()->physicalDotsPerInch() );
1229 }
1230 }
1231 else
1232 {
1233 // Fallback: compatibility with QGIS <= 3.20; always assume low dpi screens
1234 mSettings.setOutputDpi( window()->windowHandle()->screen()->logicalDotsPerInch() );
1235 mSettings.setDpiTarget( window()->windowHandle()->screen()->logicalDotsPerInch() );
1236 }
1237}
1238
1239void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange )
1240{
1241 if ( temporalRange() == dateTimeRange )
1242 return;
1243
1244 mSettings.setTemporalRange( dateTimeRange );
1245 mSettings.setIsTemporal( dateTimeRange.begin().isValid() || dateTimeRange.end().isValid() );
1246
1247 emit temporalRangeChanged();
1248
1249 // we need to discard any previously cached images which have temporal properties enabled, so that these will be updated when
1250 // the canvas is redrawn
1251 if ( !mJob )
1252 clearTemporalCache();
1253
1254 autoRefreshTriggered();
1255}
1256
1257const QgsDateTimeRange &QgsMapCanvas::temporalRange() const
1258{
1259 return mSettings.temporalRange();
1260}
1261
1263{
1264 mInteractionBlockers.append( blocker );
1265}
1266
1268{
1269 mInteractionBlockers.removeAll( blocker );
1270}
1271
1273{
1274 for ( const QgsMapCanvasInteractionBlocker *block : mInteractionBlockers )
1275 {
1276 if ( block->blockCanvasInteraction( interaction ) )
1277 return false;
1278 }
1279 return true;
1280}
1281
1282void QgsMapCanvas::mapUpdateTimeout()
1283{
1284 if ( mJob )
1285 {
1286 const QImage &img = mJob->renderedImage();
1287 mMap->setContent( img, imageRect( img, mSettings ) );
1288 }
1289}
1290
1292{
1293 if ( mJob )
1294 {
1295 QgsDebugMsgLevel( QStringLiteral( "CANVAS stop rendering!" ), 2 );
1296 mJobCanceled = true;
1297 disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
1298 connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
1299 mJob->cancelWithoutBlocking();
1300 mJob = nullptr;
1301 emit mapRefreshCanceled();
1302 }
1303 stopPreviewJobs();
1304}
1305
1306//the format defaults to "PNG" if not specified
1307void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
1308{
1309 QPainter painter;
1310 QImage image;
1311
1312 //
1313 //check if the optional QPaintDevice was supplied
1314 //
1315 if ( theQPixmap )
1316 {
1317 image = theQPixmap->toImage();
1318 painter.begin( &image );
1319
1320 // render
1321 QgsMapRendererCustomPainterJob job( mSettings, &painter );
1322 job.start();
1323 job.waitForFinished();
1324 emit renderComplete( &painter );
1325 }
1326 else //use the map view
1327 {
1328 image = mMap->contentImage().copy();
1329 painter.begin( &image );
1330 }
1331
1332 // draw annotations
1333 QStyleOptionGraphicsItem option;
1334 option.initFrom( this );
1335 QGraphicsItem *item = nullptr;
1336 QListIterator<QGraphicsItem *> i( items() );
1337 i.toBack();
1338 while ( i.hasPrevious() )
1339 {
1340 item = i.previous();
1341
1342 if ( !( item && dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) ) )
1343 {
1344 continue;
1345 }
1346
1347 QgsScopedQPainterState painterState( &painter );
1348
1349 QPointF itemScenePos = item->scenePos();
1350 painter.translate( itemScenePos.x(), itemScenePos.y() );
1351
1352 item->paint( &painter, &option );
1353 }
1354
1355 painter.end();
1356 image.save( fileName, format.toLocal8Bit().data() );
1357
1358 QFileInfo myInfo = QFileInfo( fileName );
1359
1360 // build the world file name
1361 QString outputSuffix = myInfo.suffix();
1362 QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.completeBaseName() + '.'
1363 + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
1364 QFile myWorldFile( myWorldFileName );
1365 if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
1366 {
1367 return;
1368 }
1369 QTextStream myStream( &myWorldFile );
1371}
1372
1374{
1375 return mapSettings().visibleExtent();
1376}
1377
1379{
1380 return QgsMapLayerUtils::combinedExtent( mSettings.layers(), mapSettings().destinationCrs(), QgsProject::instance()->transformContext() );
1381}
1382
1384{
1386 QgsCoordinateTransform ct( extent.crs(), mapSettings().destinationCrs(), mProject ? mProject->transformContext() : QgsProject::instance()->transformContext() );
1388 QgsRectangle rect;
1389 try
1390 {
1391 rect = ct.transformBoundingBox( extent );
1392 }
1393 catch ( QgsCsException & )
1394 {
1395 rect = mapSettings().fullExtent();
1396 }
1397
1398 return rect;
1399}
1400
1401void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
1402{
1403 QgsRectangle current = extent();
1404
1405 if ( ( r == current ) && magnified )
1406 return;
1407
1408 if ( r.isEmpty() )
1409 {
1410 if ( !mSettings.hasValidSettings() )
1411 {
1412 // we can't even just move the map center
1413 QgsDebugMsgLevel( QStringLiteral( "Empty extent - ignoring" ), 2 );
1414 return;
1415 }
1416
1417 // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
1418 QgsDebugMsgLevel( QStringLiteral( "Empty extent - keeping old scale with new center!" ), 2 );
1419 setCenter( r.center() );
1420 }
1421 else
1422 {
1423 // If scale is locked we need to maintain the current scale, so we
1424 // - magnify and recenter the map
1425 // - restore locked scale
1426 if ( mScaleLocked && magnified )
1427 {
1428 ScaleRestorer restorer( this );
1429 const double ratio { extent().width() / extent().height() };
1430 const double factor { r.width() / r.height() > ratio ? extent().width() / r.width() : extent().height() / r.height() };
1431 const double scaleFactor { std::clamp( mSettings.magnificationFactor() * factor, QgsGuiUtils::CANVAS_MAGNIFICATION_MIN, QgsGuiUtils::CANVAS_MAGNIFICATION_MAX ) };
1432 const QgsPointXY newCenter { r.center() };
1433 mSettings.setMagnificationFactor( scaleFactor, &newCenter );
1434 emit magnificationChanged( scaleFactor );
1435 }
1436 else
1437 {
1438 mSettings.setExtent( r, magnified );
1439 }
1440 }
1441 emit extentsChanged();
1442 updateScale();
1443
1444 //clear all extent items after current index
1445 for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
1446 {
1447 mLastExtent.removeAt( i );
1448 }
1449
1450 if ( !mLastExtent.isEmpty() && mLastExtent.last() != extent() )
1451 {
1452 mLastExtent.append( extent() );
1453 }
1454
1455 // adjust history to no more than 100
1456 if ( mLastExtent.size() > 100 )
1457 {
1458 mLastExtent.removeAt( 0 );
1459 }
1460
1461 // the last item is the current extent
1462 mLastExtentIndex = mLastExtent.size() - 1;
1463
1464 // update controls' enabled state
1465 emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1466 emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1467}
1468
1470{
1471 QgsRectangle canvasExtent = extent;
1472 if ( extent.crs() != mapSettings().destinationCrs() )
1473 {
1474 QgsCoordinateTransform ct( extent.crs(), mapSettings().destinationCrs(), QgsProject::instance() );
1476 canvasExtent = ct.transformBoundingBox( extent );
1477
1478 if ( canvasExtent.isEmpty() )
1479 {
1480 return false;
1481 }
1482 }
1483
1484 setExtent( canvasExtent, true );
1485 return true;
1486}
1487
1489{
1490 const QgsRectangle r = mapSettings().extent();
1491 const double xMin = center.x() - r.width() / 2.0;
1492 const double yMin = center.y() - r.height() / 2.0;
1493 const QgsRectangle rect(
1494 xMin, yMin,
1495 xMin + r.width(), yMin + r.height()
1496 );
1497 if ( ! rect.isEmpty() )
1498 {
1499 setExtent( rect, true );
1500 }
1501} // setCenter
1502
1504{
1506 return r.center();
1507}
1508
1509QgsPointXY QgsMapCanvas::cursorPoint() const
1510{
1511 return mCursorPoint;
1512}
1513
1515{
1516 return mapSettings().rotation();
1517} // rotation
1518
1519void QgsMapCanvas::setRotation( double degrees )
1520{
1521 double current = rotation();
1522
1523 if ( qgsDoubleNear( degrees, current ) )
1524 return;
1525
1526 mSettings.setRotation( degrees );
1527 emit rotationChanged( degrees );
1528 emit extentsChanged(); // visible extent changes with rotation
1529} // setRotation
1530
1531
1533{
1534 emit scaleChanged( mapSettings().scale() );
1535}
1536
1538{
1540 // If the full extent is an empty set, don't do the zoom
1541 if ( !extent.isEmpty() )
1542 {
1543 // Add a 5% margin around the full extent
1544 extent.scale( 1.05 );
1545 setExtent( extent );
1546 }
1547 refresh();
1548}
1549
1551{
1553
1554 // If the full extent is an empty set, don't do the zoom
1555 if ( !extent.isEmpty() )
1556 {
1557 // Add a 5% margin around the full extent
1558 extent.scale( 1.05 );
1559 setExtent( extent );
1560 }
1561 refresh();
1562}
1563
1565{
1566 if ( mLastExtentIndex > 0 )
1567 {
1568 mLastExtentIndex--;
1569 mSettings.setExtent( mLastExtent[mLastExtentIndex] );
1570 emit extentsChanged();
1571 updateScale();
1572 refresh();
1573 // update controls' enabled state
1574 emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1575 emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1576 }
1577
1578} // zoomToPreviousExtent
1579
1581{
1582 if ( mLastExtentIndex < mLastExtent.size() - 1 )
1583 {
1584 mLastExtentIndex++;
1585 mSettings.setExtent( mLastExtent[mLastExtentIndex] );
1586 emit extentsChanged();
1587 updateScale();
1588 refresh();
1589 // update controls' enabled state
1590 emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1591 emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1592 }
1593}// zoomToNextExtent
1594
1596{
1597 mLastExtent.clear(); // clear the zoom history list
1598 mLastExtent.append( extent() ) ; // set the current extent in the list
1599 mLastExtentIndex = mLastExtent.size() - 1;
1600 // update controls' enabled state
1601 emit zoomLastStatusChanged( mLastExtentIndex > 0 );
1602 emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
1603}// clearExtentHistory
1604
1605QgsRectangle QgsMapCanvas::optimalExtentForPointLayer( QgsVectorLayer *layer, const QgsPointXY &center, int scaleFactor )
1606{
1607 QgsRectangle rect( center, center );
1608
1609 if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
1610 {
1611 QgsPointXY centerLayerCoordinates = mSettings.mapToLayerCoordinates( layer, center );
1612 QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, &centerLayerCoordinates );
1614 QgsFeatureIterator fit = layer->getFeatures( req );
1615 QgsFeature f;
1616 QgsPointXY closestPoint;
1617 double closestSquaredDistance = pow( extentRect.width(), 2.0 ) + pow( extentRect.height(), 2.0 );
1618 bool pointFound = false;
1619 while ( fit.nextFeature( f ) )
1620 {
1621 QgsPointXY point = f.geometry().asPoint();
1622 double sqrDist = point.sqrDist( centerLayerCoordinates );
1623 if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits<double>::epsilon() )
1624 continue;
1625 pointFound = true;
1626 closestPoint = point;
1627 closestSquaredDistance = sqrDist;
1628 }
1629 if ( pointFound )
1630 {
1631 // combine selected point with closest point and scale this rect
1632 rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) );
1633 rect.scale( scaleFactor, &center );
1634 }
1635 }
1636 return rect;
1637}
1638
1640{
1641 QgsTemporaryCursorOverride cursorOverride( Qt::WaitCursor );
1642
1643 if ( !layer )
1644 {
1645 // use current layer by default
1646 layer = mCurrentLayer;
1647 }
1648
1649 if ( !layer || !layer->isSpatial() )
1650 return;
1651
1652 QgsRectangle rect;
1653
1654 switch ( layer->type() )
1655 {
1657 {
1658 QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( layer );
1659 if ( vlayer->selectedFeatureCount() == 0 )
1660 return;
1661
1662 rect = vlayer->boundingBoxOfSelected();
1663 if ( rect.isNull() )
1664 {
1665 cursorOverride.release();
1666 emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1667 return;
1668 }
1669
1671
1672 // zoom in if point cannot be distinguished from others
1673 // also check that rect is empty, as it might not in case of multi points
1674 if ( vlayer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1675 {
1676 rect = optimalExtentForPointLayer( vlayer, rect.center() );
1677 }
1678 break;
1679 }
1680
1682 {
1683 QgsVectorTileLayer *vtLayer = qobject_cast< QgsVectorTileLayer * >( layer );
1684 if ( vtLayer->selectedFeatureCount() == 0 )
1685 return;
1686
1687 const QList< QgsFeature > selectedFeatures = vtLayer->selectedFeatures();
1688 for ( const QgsFeature &feature : selectedFeatures )
1689 {
1690 if ( !feature.hasGeometry() )
1691 continue;
1692
1693 rect.combineExtentWith( feature.geometry().boundingBox() );
1694 }
1695
1696 if ( rect.isNull() )
1697 {
1698 cursorOverride.release();
1699 emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1700 return;
1701 }
1702
1704 break;
1705 }
1706
1713 return; // not supported
1714 }
1715
1716 zoomToFeatureExtent( rect );
1717}
1718
1719void QgsMapCanvas::zoomToSelected( const QList<QgsMapLayer *> &layers )
1720{
1721 QgsRectangle rect;
1722 rect.setMinimal();
1723 QgsRectangle selectionExtent;
1724 selectionExtent.setMinimal();
1725
1726 for ( QgsMapLayer *mapLayer : layers )
1727 {
1728 if ( !mapLayer || !mapLayer->isSpatial() )
1729 continue;
1730
1731 switch ( mapLayer->type() )
1732 {
1734 {
1735 QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mapLayer );
1736
1737 if ( layer->selectedFeatureCount() == 0 )
1738 continue;
1739
1740 rect = layer->boundingBoxOfSelected();
1741
1742 if ( rect.isNull() )
1743 continue;
1744
1746
1747 if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
1748 rect = optimalExtentForPointLayer( layer, rect.center() );
1749
1750 selectionExtent.combineExtentWith( rect );
1751 break;
1752 }
1753
1755 {
1756 QgsVectorTileLayer *vtLayer = qobject_cast< QgsVectorTileLayer * >( mapLayer );
1757 if ( vtLayer->selectedFeatureCount() == 0 )
1758 continue;
1759
1760 const QList< QgsFeature > selectedFeatures = vtLayer->selectedFeatures();
1761 QgsRectangle rect;
1762 for ( const QgsFeature &feature : selectedFeatures )
1763 {
1764 if ( !feature.hasGeometry() )
1765 continue;
1766
1767 rect.combineExtentWith( feature.geometry().boundingBox() );
1768 }
1769
1770 rect = mapSettings().layerExtentToOutputExtent( vtLayer, rect );
1771 selectionExtent.combineExtentWith( rect );
1772 break;
1773 }
1774
1781 break;
1782 }
1783 }
1784
1785 if ( selectionExtent.isNull() )
1786 {
1787 emit messageEmitted( tr( "Cannot zoom to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1788 return;
1789 }
1790
1791 zoomToFeatureExtent( selectionExtent );
1792}
1793
1795{
1796 return mSettings.zRange();
1797}
1798
1800{
1801 if ( zRange() == range )
1802 return;
1803
1804 mSettings.setZRange( range );
1805
1806 emit zRangeChanged();
1807
1808 // we need to discard any previously cached images which are elevation aware, so that these will be updated when
1809 // the canvas is redrawn
1810 if ( !mJob )
1811 clearElevationCache();
1812
1813 autoRefreshTriggered();
1814}
1815
1817{
1818 // no selected features, only one selected point feature
1819 //or two point features with the same x- or y-coordinates
1820 if ( rect.isEmpty() )
1821 {
1822 // zoom in
1823 QgsPointXY c = rect.center();
1824 rect = extent();
1825 rect.scale( 1.0, &c );
1826 }
1827 //zoom to an area
1828 else
1829 {
1830 // Expand rect to give a bit of space around the selected
1831 // objects so as to keep them clear of the map boundaries
1832 // The same 5% should apply to all margins.
1833 rect.scale( 1.05 );
1834 }
1835
1836 setExtent( rect );
1837 refresh();
1838}
1839
1841{
1842 if ( !layer )
1843 {
1844 return;
1845 }
1846
1847 QgsRectangle bbox;
1848 QString errorMsg;
1849 if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1850 {
1851 if ( bbox.isEmpty() )
1852 {
1853 bbox = optimalExtentForPointLayer( layer, bbox.center() );
1854 }
1855 zoomToFeatureExtent( bbox );
1856 }
1857 else
1858 {
1859 emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, Qgis::MessageLevel::Warning );
1860 }
1861
1862}
1863
1864void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter )
1865{
1866 if ( !layer )
1867 {
1868 return;
1869 }
1870
1871 QgsRectangle bbox;
1872 QString errorMsg;
1873 if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
1874 {
1875 if ( alwaysRecenter || !mapSettings().extent().contains( bbox ) )
1876 setCenter( bbox.center() );
1877 refresh();
1878 }
1879 else
1880 {
1881 emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, Qgis::MessageLevel::Warning );
1882 }
1883}
1884
1885bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1886{
1887 QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
1888 bbox.setMinimal();
1889 QgsFeature fet;
1890 int featureCount = 0;
1891 errorMsg.clear();
1892
1893 while ( it.nextFeature( fet ) )
1894 {
1895 QgsGeometry geom = fet.geometry();
1896 if ( geom.isNull() )
1897 {
1898 errorMsg = tr( "Feature does not have a geometry" );
1899 }
1900 else if ( geom.constGet()->isEmpty() )
1901 {
1902 errorMsg = tr( "Feature geometry is empty" );
1903 }
1904 if ( !errorMsg.isEmpty() )
1905 {
1906 return false;
1907 }
1909 bbox.combineExtentWith( r );
1910 featureCount++;
1911 }
1912
1913 if ( featureCount != ids.count() )
1914 {
1915 errorMsg = tr( "Feature not found" );
1916 return false;
1917 }
1918
1919 return true;
1920}
1921
1923{
1924 if ( !layer )
1925 {
1926 // use current layer by default
1927 layer = mCurrentLayer;
1928 }
1929 if ( !layer || !layer->isSpatial() )
1930 return;
1931
1932 QgsRectangle rect;
1933 switch ( layer->type() )
1934 {
1936 {
1937 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
1938 if ( vLayer->selectedFeatureCount() == 0 )
1939 return;
1940
1941 rect = vLayer->boundingBoxOfSelected();
1942 break;
1943 }
1945 {
1946 QgsVectorTileLayer *vtLayer = qobject_cast< QgsVectorTileLayer * >( layer );
1947 if ( vtLayer->selectedFeatureCount() == 0 )
1948 return;
1949
1950 const QList< QgsFeature > selectedFeatures = vtLayer->selectedFeatures();
1951 for ( const QgsFeature &feature : selectedFeatures )
1952 {
1953 if ( !feature.hasGeometry() )
1954 continue;
1955
1956 rect.combineExtentWith( feature.geometry().boundingBox() );
1957 }
1958 break;
1959 }
1960
1967 return;
1968 }
1969
1970 if ( rect.isNull() )
1971 {
1972 emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
1973 return;
1974 }
1975
1977 setCenter( rect.center() );
1978 refresh();
1979}
1980
1981void QgsMapCanvas::panToSelected( const QList<QgsMapLayer *> &layers )
1982{
1983 QgsRectangle selectionExtent;
1984 selectionExtent.setMinimal();
1985
1986 for ( QgsMapLayer *mapLayer : layers )
1987 {
1988 if ( !mapLayer || !mapLayer->isSpatial() )
1989 continue;
1990
1991 QgsRectangle rect;
1992 rect.setMinimal();
1993 switch ( mapLayer->type() )
1994 {
1996 {
1997 QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mapLayer );
1998 if ( layer->selectedFeatureCount() == 0 )
1999 continue;
2000
2001 rect = layer->boundingBoxOfSelected();
2002
2003 if ( rect.isNull() )
2004 continue;
2005
2007
2008 if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() )
2009 rect = optimalExtentForPointLayer( layer, rect.center() );
2010 break;
2011 }
2012
2014 {
2015 QgsVectorTileLayer *vtLayer = qobject_cast< QgsVectorTileLayer * >( mapLayer );
2016 if ( vtLayer->selectedFeatureCount() == 0 )
2017 continue;
2018
2019 const QList< QgsFeature > selectedFeatures = vtLayer->selectedFeatures();
2020 for ( const QgsFeature &feature : selectedFeatures )
2021 {
2022 if ( !feature.hasGeometry() )
2023 continue;
2024
2025 rect.combineExtentWith( feature.geometry().boundingBox() );
2026 }
2027
2028 rect = mapSettings().layerExtentToOutputExtent( vtLayer, rect );
2029 break;
2030 }
2031
2038 continue;
2039 }
2040
2041 selectionExtent.combineExtentWith( rect );
2042 }
2043
2044 if ( selectionExtent.isNull() )
2045 {
2046 emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "No extent could be determined." ), Qgis::MessageLevel::Warning );
2047 return;
2048 }
2049
2050 setCenter( selectionExtent.center() );
2051 refresh();
2052}
2053
2055 const QColor &color1, const QColor &color2,
2056 int flashes, int duration )
2057{
2058 if ( !layer )
2059 {
2060 return;
2061 }
2062
2063 QList< QgsGeometry > geoms;
2064
2065 QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setNoAttributes() );
2066 QgsFeature fet;
2067 while ( it.nextFeature( fet ) )
2068 {
2069 if ( !fet.hasGeometry() )
2070 continue;
2071 geoms << fet.geometry();
2072 }
2073
2074 flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
2075}
2076
2077void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
2078{
2079 if ( geometries.isEmpty() )
2080 return;
2081
2082 QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
2083 QgsRubberBand *rb = new QgsRubberBand( this, geomType );
2084 for ( const QgsGeometry &geom : geometries )
2085 rb->addGeometry( geom, crs, false );
2086 rb->updatePosition();
2087 rb->update();
2088
2089 if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
2090 {
2091 rb->setWidth( 2 );
2092 rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
2093 }
2094 if ( geomType == QgsWkbTypes::PointGeometry )
2096
2097 QColor startColor = color1;
2098 if ( !startColor.isValid() )
2099 {
2100 if ( geomType == QgsWkbTypes::PolygonGeometry )
2101 {
2102 startColor = rb->fillColor();
2103 }
2104 else
2105 {
2106 startColor = rb->strokeColor();
2107 }
2108 startColor.setAlpha( 255 );
2109 }
2110 QColor endColor = color2;
2111 if ( !endColor.isValid() )
2112 {
2113 endColor = startColor;
2114 endColor.setAlpha( 0 );
2115 }
2116
2117
2118 QVariantAnimation *animation = new QVariantAnimation( this );
2119 connect( animation, &QVariantAnimation::finished, this, [animation, rb]
2120 {
2121 animation->deleteLater();
2122 delete rb;
2123 } );
2124 connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
2125 {
2126 QColor c = value.value<QColor>();
2127 if ( geomType == QgsWkbTypes::PolygonGeometry )
2128 {
2129 rb->setFillColor( c );
2130 }
2131 else
2132 {
2133 rb->setStrokeColor( c );
2134 QColor c = rb->secondaryStrokeColor();
2135 c.setAlpha( c.alpha() );
2137 }
2138 rb->update();
2139 } );
2140
2141 animation->setDuration( duration * flashes );
2142 animation->setStartValue( endColor );
2143 double midStep = 0.2 / flashes;
2144 for ( int i = 0; i < flashes; ++i )
2145 {
2146 double start = static_cast< double >( i ) / flashes;
2147 animation->setKeyValueAt( start + midStep, startColor );
2148 double end = static_cast< double >( i + 1 ) / flashes;
2149 if ( !qgsDoubleNear( end, 1.0 ) )
2150 animation->setKeyValueAt( end, endColor );
2151 }
2152 animation->setEndValue( endColor );
2153 animation->start();
2154}
2155
2157{
2158 if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
2159 {
2160 emit keyPressed( e );
2161 return;
2162 }
2163
2164 // Don't want to interfer with mouse events
2165 if ( ! mCanvasProperties->mouseButtonDown )
2166 {
2167 // this is backwards, but we can't change now without breaking api because
2168 // forever QgsMapTools have had to explicitly mark events as ignored in order to
2169 // indicate that they've consumed the event and that the default behavior should not
2170 // be applied..!
2171 e->accept();
2172 if ( mMapTool )
2173 {
2174 mMapTool->keyPressEvent( e );
2175 if ( !e->isAccepted() ) // map tool consumed event
2176 return;
2177 }
2178
2179 QgsRectangle currentExtent = mapSettings().visibleExtent();
2180 double dx = std::fabs( currentExtent.width() / 4 );
2181 double dy = std::fabs( currentExtent.height() / 4 );
2182
2183 switch ( e->key() )
2184 {
2185 case Qt::Key_Left:
2186 QgsDebugMsgLevel( QStringLiteral( "Pan left" ), 2 );
2187 setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
2188 refresh();
2189 break;
2190
2191 case Qt::Key_Right:
2192 QgsDebugMsgLevel( QStringLiteral( "Pan right" ), 2 );
2193 setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
2194 refresh();
2195 break;
2196
2197 case Qt::Key_Up:
2198 QgsDebugMsgLevel( QStringLiteral( "Pan up" ), 2 );
2199 setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
2200 refresh();
2201 break;
2202
2203 case Qt::Key_Down:
2204 QgsDebugMsgLevel( QStringLiteral( "Pan down" ), 2 );
2205 setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
2206 refresh();
2207 break;
2208
2209 case Qt::Key_Space:
2210 QgsDebugMsgLevel( QStringLiteral( "Pressing pan selector" ), 2 );
2211
2212 //mCanvasProperties->dragging = true;
2213 if ( ! e->isAutoRepeat() )
2214 {
2215 mTemporaryCursorOverride.reset( new QgsTemporaryCursorOverride( Qt::ClosedHandCursor ) );
2216 mCanvasProperties->panSelectorDown = true;
2217 panActionStart( mCanvasProperties->mouseLastXY );
2218 }
2219 break;
2220
2221 case Qt::Key_PageUp:
2222 QgsDebugMsgLevel( QStringLiteral( "Zoom in" ), 2 );
2223 zoomIn();
2224 break;
2225
2226 case Qt::Key_PageDown:
2227 QgsDebugMsgLevel( QStringLiteral( "Zoom out" ), 2 );
2228 zoomOut();
2229 break;
2230
2231#if 0
2232 case Qt::Key_P:
2233 mUseParallelRendering = !mUseParallelRendering;
2234 refresh();
2235 break;
2236
2237 case Qt::Key_S:
2238 mDrawRenderingStats = !mDrawRenderingStats;
2239 refresh();
2240 break;
2241#endif
2242
2243 default:
2244 // Pass it on
2245 if ( !mMapTool )
2246 {
2247 e->ignore();
2248 QgsDebugMsgLevel( "Ignoring key: " + QString::number( e->key() ), 2 );
2249 }
2250 }
2251 }
2252
2253 emit keyPressed( e );
2254}
2255
2257{
2258 QgsDebugMsgLevel( QStringLiteral( "keyRelease event" ), 2 );
2259
2260 switch ( e->key() )
2261 {
2262 case Qt::Key_Space:
2263 if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
2264 {
2265 QgsDebugMsgLevel( QStringLiteral( "Releasing pan selector" ), 2 );
2266 mTemporaryCursorOverride.reset();
2267 mCanvasProperties->panSelectorDown = false;
2268 panActionEnd( mCanvasProperties->mouseLastXY );
2269 }
2270 break;
2271
2272 default:
2273 // Pass it on
2274 if ( mMapTool )
2275 {
2276 mMapTool->keyReleaseEvent( e );
2277 }
2278 else e->ignore();
2279
2280 QgsDebugMsgLevel( "Ignoring key release: " + QString::number( e->key() ), 2 );
2281 }
2282
2283 emit keyReleased( e );
2284
2285} //keyReleaseEvent()
2286
2287
2289{
2290 // call handler of current map tool
2291 if ( mMapTool )
2292 {
2293 std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2294 mMapTool->canvasDoubleClickEvent( me.get() );
2295 }
2296}// mouseDoubleClickEvent
2297
2298
2299void QgsMapCanvas::beginZoomRect( QPoint pos )
2300{
2301 mZoomRect.setRect( 0, 0, 0, 0 );
2302 mTemporaryCursorOverride.reset( new QgsTemporaryCursorOverride( mZoomCursor ) );
2303 mZoomDragging = true;
2304 mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
2305 QColor color( Qt::blue );
2306 color.setAlpha( 63 );
2307 mZoomRubberBand->setColor( color );
2308 mZoomRect.setTopLeft( pos );
2309}
2310
2311void QgsMapCanvas::stopZoomRect()
2312{
2313 if ( mZoomDragging )
2314 {
2315 mZoomDragging = false;
2316 mZoomRubberBand.reset( nullptr );
2317 mTemporaryCursorOverride.reset();
2318 }
2319}
2320
2321void QgsMapCanvas::endZoomRect( QPoint pos )
2322{
2323 stopZoomRect();
2324
2325 // store the rectangle
2326 mZoomRect.setRight( pos.x() );
2327 mZoomRect.setBottom( pos.y() );
2328
2329 //account for bottom right -> top left dragging
2330 mZoomRect = mZoomRect.normalized();
2331
2332 if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
2333 {
2334 //probably a mistake - would result in huge zoom!
2335 return;
2336 }
2337
2338 // set center and zoom
2339 const QSize &zoomRectSize = mZoomRect.size();
2340 const QSize &canvasSize = mSettings.outputSize();
2341 double sfx = static_cast< double >( zoomRectSize.width() ) / canvasSize.width();
2342 double sfy = static_cast< double >( zoomRectSize.height() ) / canvasSize.height();
2343 double sf = std::max( sfx, sfy );
2344
2345 QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
2346
2347 zoomByFactor( sf, &c );
2348 refresh();
2349}
2350
2351void QgsMapCanvas::startPan()
2352{
2353 if ( !mCanvasProperties->panSelectorDown )
2354 {
2355 mCanvasProperties->panSelectorDown = true;
2356 mTemporaryCursorOverride.reset( new QgsTemporaryCursorOverride( Qt::ClosedHandCursor ) );
2357 panActionStart( mCanvasProperties->mouseLastXY );
2358 }
2359}
2360
2361void QgsMapCanvas::stopPan()
2362{
2363 if ( mCanvasProperties->panSelectorDown )
2364 {
2365 mCanvasProperties->panSelectorDown = false;
2366 mTemporaryCursorOverride.reset();
2367 panActionEnd( mCanvasProperties->mouseLastXY );
2368 }
2369}
2370
2371void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
2372{
2373 // use shift+middle mouse button for zooming, map tools won't receive any events in that case
2374 if ( e->button() == Qt::MiddleButton &&
2375 e->modifiers() & Qt::ShiftModifier )
2376 {
2377 beginZoomRect( e->pos() );
2378 return;
2379 }
2380 //use middle mouse button for panning, map tools won't receive any events in that case
2381 else if ( e->button() == Qt::MiddleButton )
2382 {
2383 startPan();
2384 }
2385 else
2386 {
2387 // If doing a middle-button-click, followed by a right-button-click,
2388 // cancel the pan or zoomRect action started above.
2389 stopPan();
2390 stopZoomRect();
2391
2392 // call handler of current map tool
2393 if ( mMapTool )
2394 {
2395 if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
2396 && e->modifiers() & Qt::ShiftModifier )
2397 {
2398 beginZoomRect( e->pos() );
2399 return;
2400 }
2401 else if ( mMapTool->flags() & QgsMapTool::ShowContextMenu && e->button() == Qt::RightButton )
2402 {
2403 std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2404 showContextMenu( me.get() );
2405 return;
2406 }
2407 else
2408 {
2409 std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2410 mMapTool->canvasPressEvent( me.get() );
2411 }
2412 }
2413 }
2414
2415 if ( mCanvasProperties->panSelectorDown )
2416 {
2417 return;
2418 }
2419
2420 mCanvasProperties->mouseButtonDown = true;
2421 mCanvasProperties->rubberStartPoint = e->pos();
2422}
2423
2425{
2426 // if using shift+middle mouse button for zooming, end zooming and return
2427 if ( mZoomDragging &&
2428 e->button() == Qt::MiddleButton )
2429 {
2430 endZoomRect( e->pos() );
2431 return;
2432 }
2433 //use middle mouse button for panning, map tools won't receive any events in that case
2434 else if ( e->button() == Qt::MiddleButton )
2435 {
2436 stopPan();
2437 }
2438 else if ( e->button() == Qt::BackButton )
2439 {
2441 return;
2442 }
2443 else if ( e->button() == Qt::ForwardButton )
2444 {
2446 return;
2447 }
2448 else
2449 {
2450 if ( mZoomDragging && e->button() == Qt::LeftButton )
2451 {
2452 endZoomRect( e->pos() );
2453 return;
2454 }
2455
2456 // call handler of current map tool
2457 if ( mMapTool )
2458 {
2459 // right button was pressed in zoom tool? return to previous non zoom tool
2460 if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
2461 {
2462 QgsDebugMsgLevel( QStringLiteral( "Right click in map tool zoom or pan, last tool is %1." ).arg(
2463 mLastNonZoomMapTool ? QStringLiteral( "not null" ) : QStringLiteral( "null" ) ), 2 );
2464
2465 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
2466
2467 // change to older non-zoom tool
2468 if ( mLastNonZoomMapTool
2469 && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
2470 || ( vlayer && vlayer->isEditable() ) ) )
2471 {
2472 QgsMapTool *t = mLastNonZoomMapTool;
2473 mLastNonZoomMapTool = nullptr;
2474 setMapTool( t );
2475 }
2476 return;
2477 }
2478 std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2479 mMapTool->canvasReleaseEvent( me.get() );
2480 }
2481 }
2482
2483
2484 mCanvasProperties->mouseButtonDown = false;
2485
2486 if ( mCanvasProperties->panSelectorDown )
2487 return;
2488
2489}
2490
2491void QgsMapCanvas::resizeEvent( QResizeEvent *e )
2492{
2493 QGraphicsView::resizeEvent( e );
2494 mResizeTimer->start( 500 ); // in charge of refreshing canvas
2495
2496 double oldScale = mSettings.scale();
2497 QSize lastSize = viewport()->size();
2498 mSettings.setOutputSize( lastSize );
2499
2500 mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
2501
2502 moveCanvasContents( true );
2503
2504 if ( mScaleLocked )
2505 {
2506 double scaleFactor = oldScale / mSettings.scale();
2507 QgsRectangle r = mSettings.extent();
2508 QgsPointXY center = r.center();
2509 r.scale( scaleFactor, &center );
2510 mSettings.setExtent( r );
2511 }
2512 else
2513 {
2514 updateScale();
2515 }
2516
2517 emit extentsChanged();
2518}
2519
2520void QgsMapCanvas::paintEvent( QPaintEvent *e )
2521{
2522 // no custom event handling anymore
2523
2524 QGraphicsView::paintEvent( e );
2525} // paintEvent
2526
2528{
2529 if ( mBlockItemPositionUpdates )
2530 return;
2531
2532 const QList<QGraphicsItem *> items = mScene->items();
2533 for ( QGraphicsItem *gi : items )
2534 {
2535 QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( gi );
2536
2537 if ( item )
2538 {
2539 item->updatePosition();
2540 }
2541 }
2542}
2543
2544
2545void QgsMapCanvas::wheelEvent( QWheelEvent *e )
2546{
2547 // Zoom the map canvas in response to a mouse wheel event. Moving the
2548 // wheel forward (away) from the user zooms in
2549
2550 QgsDebugMsgLevel( "Wheel event delta " + QString::number( e->angleDelta().y() ), 2 );
2551
2552 if ( mMapTool )
2553 {
2554 mMapTool->wheelEvent( e );
2555 if ( e->isAccepted() )
2556 return;
2557 }
2558
2559 if ( e->angleDelta().y() == 0 )
2560 {
2561 e->accept();
2562 return;
2563 }
2564
2565 double zoomFactor = e->angleDelta().y() > 0 ? 1. / zoomInFactor() : zoomOutFactor();
2566
2567 // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
2568 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
2569
2570 if ( e->modifiers() & Qt::ControlModifier )
2571 {
2572 //holding ctrl while wheel zooming results in a finer zoom
2573 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
2574 }
2575
2576 double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
2577
2578 // zoom map to mouse cursor by scaling
2579 QgsPointXY oldCenter = center();
2580#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
2581 QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->pos().x(), e->pos().y() ) );
2582#else
2583 QgsPointXY mousePos( getCoordinateTransform()->toMapCoordinates( e->position().x(), e->position().y() ) );
2584#endif
2585 QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
2586 mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
2587
2588 zoomByFactor( signedWheelFactor, &newCenter );
2589 e->accept();
2590}
2591
2592void QgsMapCanvas::setWheelFactor( double factor )
2593{
2594 mWheelZoomFactor = factor;
2595}
2596
2598{
2599 // magnification is alreday handled in zoomByFactor
2601}
2602
2604{
2605 // magnification is alreday handled in zoomByFactor
2607}
2608
2609void QgsMapCanvas::zoomScale( double newScale, bool ignoreScaleLock )
2610{
2611 zoomByFactor( newScale / scale(), nullptr, ignoreScaleLock );
2612}
2613
2614void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
2615{
2616 double scaleFactor = ( zoomIn ? zoomInFactor() : zoomOutFactor() );
2617
2618 // transform the mouse pos to map coordinates
2620
2621 if ( mScaleLocked )
2622 {
2623 ScaleRestorer restorer( this );
2625 }
2626 else
2627 {
2629 r.scale( scaleFactor, &center );
2630 setExtent( r, true );
2631 refresh();
2632 }
2633}
2634
2635void QgsMapCanvas::setScaleLocked( bool isLocked )
2636{
2637 if ( mScaleLocked != isLocked )
2638 {
2639 mScaleLocked = isLocked;
2640 emit scaleLockChanged( mScaleLocked );
2641 }
2642}
2643
2644void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
2645{
2646 mCanvasProperties->mouseLastXY = e->pos();
2647
2648 if ( mCanvasProperties->panSelectorDown )
2649 {
2650 panAction( e );
2651 }
2652 else if ( mZoomDragging )
2653 {
2654 mZoomRect.setBottomRight( e->pos() );
2655 mZoomRubberBand->setToCanvasRectangle( mZoomRect );
2656 mZoomRubberBand->show();
2657 }
2658 else
2659 {
2660 // call handler of current map tool
2661 if ( mMapTool )
2662 {
2663 std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
2664 mMapTool->canvasMoveEvent( me.get() );
2665 }
2666 }
2667
2668 // show x y on status bar (if we are mid pan operation, then the cursor point hasn't changed!)
2669 if ( !panOperationInProgress() )
2670 {
2671 mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
2672 emit xyCoordinates( mCursorPoint );
2673 }
2674}
2675
2676void QgsMapCanvas::setMapTool( QgsMapTool *tool, bool clean )
2677{
2678 if ( !tool )
2679 return;
2680
2681 if ( mMapTool )
2682 {
2683 if ( clean )
2684 mMapTool->clean();
2685
2686 disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
2687 mMapTool->deactivate();
2688 }
2689
2690 if ( ( tool->flags() & QgsMapTool::Transient )
2691 && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
2692 {
2693 // if zoom or pan tool will be active, save old tool
2694 // to bring it back on right click
2695 // (but only if it wasn't also zoom or pan tool)
2696 mLastNonZoomMapTool = mMapTool;
2697 }
2698 else
2699 {
2700 mLastNonZoomMapTool = nullptr;
2701 }
2702
2703 QgsMapTool *oldTool = mMapTool;
2704
2705 // set new map tool and activate it
2706 mMapTool = tool;
2707 emit mapToolSet( mMapTool, oldTool );
2708 if ( mMapTool )
2709 {
2710 connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
2711 mMapTool->activate();
2712 }
2713
2714} // setMapTool
2715
2717{
2718 if ( mMapTool && mMapTool == tool )
2719 {
2720 disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
2721 QgsMapTool *oldTool = mMapTool;
2722 mMapTool = nullptr;
2723 oldTool->deactivate();
2724 emit mapToolSet( nullptr, oldTool );
2725 setCursor( Qt::ArrowCursor );
2726 }
2727
2728 if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
2729 {
2730 mLastNonZoomMapTool = nullptr;
2731 }
2732}
2733
2735{
2736 mProject = project;
2737}
2738
2739void QgsMapCanvas::setCanvasColor( const QColor &color )
2740{
2741 if ( canvasColor() == color )
2742 return;
2743
2744 // background of map's pixmap
2745 mSettings.setBackgroundColor( color );
2746
2747 // background of the QGraphicsView
2748 QBrush bgBrush( color );
2749 setBackgroundBrush( bgBrush );
2750#if 0
2751 QPalette palette;
2752 palette.setColor( backgroundRole(), color );
2753 setPalette( palette );
2754#endif
2755
2756 // background of QGraphicsScene
2757 mScene->setBackgroundBrush( bgBrush );
2758
2759 refresh();
2760
2761 emit canvasColorChanged();
2762}
2763
2765{
2766 return mScene->backgroundBrush().color();
2767}
2768
2769void QgsMapCanvas::setSelectionColor( const QColor &color )
2770{
2771 if ( mSettings.selectionColor() == color )
2772 return;
2773
2774 mSettings.setSelectionColor( color );
2775
2776 if ( mCache )
2777 {
2778 bool hasSelectedFeatures = false;
2779 const auto layers = mSettings.layers();
2780 for ( QgsMapLayer *layer : layers )
2781 {
2782 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2783 if ( vlayer && vlayer->selectedFeatureCount() )
2784 {
2785 hasSelectedFeatures = true;
2786 break;
2787 }
2788 }
2789
2790 if ( hasSelectedFeatures )
2791 {
2792 mCache->clear();
2793 refresh();
2794 }
2795 }
2796}
2797
2799{
2800 return mSettings.selectionColor();
2801}
2802
2804{
2805 return mapSettings().layers().size();
2806}
2807
2808QList<QgsMapLayer *> QgsMapCanvas::layers( bool expandGroupLayers ) const
2809{
2810 return mapSettings().layers( expandGroupLayers );
2811}
2812
2814{
2815 // called when a layer has changed visibility setting
2816 refresh();
2817}
2818
2819void QgsMapCanvas::freeze( bool frozen )
2820{
2821 mFrozen = frozen;
2822}
2823
2825{
2826 return mFrozen;
2827}
2828
2830{
2831 return mapSettings().mapUnitsPerPixel();
2832}
2833
2835{
2836 return mapSettings().mapUnits();
2837}
2838
2839QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
2840{
2841 return mSettings.layerStyleOverrides();
2842}
2843
2844void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
2845{
2846 if ( overrides == mSettings.layerStyleOverrides() )
2847 return;
2848
2849 mSettings.setLayerStyleOverrides( overrides );
2850 clearCache();
2852}
2853
2854void QgsMapCanvas::setTheme( const QString &theme )
2855{
2856 if ( mTheme == theme )
2857 return;
2858
2859 clearCache();
2860 if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
2861 {
2862 mTheme.clear();
2863 mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
2864 setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
2865 emit themeChanged( QString() );
2866 }
2867 else
2868 {
2869 mTheme = theme;
2870 setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
2871 emit themeChanged( theme );
2872 }
2873}
2874
2876{
2877 mRenderFlag = flag;
2878
2879 if ( mRenderFlag )
2880 {
2881 refresh();
2882 }
2883 else
2884 stopRendering();
2885}
2886
2887#if 0
2888void QgsMapCanvas::connectNotify( const char *signal )
2889{
2890 Q_UNUSED( signal )
2891 QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
2892} //connectNotify
2893#endif
2894
2895void QgsMapCanvas::layerRepaintRequested( bool deferred )
2896{
2897 if ( !deferred )
2898 refresh();
2899}
2900
2901void QgsMapCanvas::autoRefreshTriggered()
2902{
2903 if ( mJob )
2904 {
2905 // canvas is currently being redrawn, so we defer the last requested
2906 // auto refresh until current rendering job finishes
2907 mRefreshAfterJob = true;
2908 return;
2909 }
2910
2911 refresh();
2912}
2913
2914void QgsMapCanvas::updateAutoRefreshTimer()
2915{
2916 // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
2917 // trigger a map refresh on this minimum interval
2918 int minAutoRefreshInterval = -1;
2919 const auto layers = mSettings.layers();
2920 for ( QgsMapLayer *layer : layers )
2921 {
2922 int layerRefreshInterval = 0;
2923
2925 {
2926 layerRefreshInterval = layer->autoRefreshInterval();
2927 }
2928 else if ( QgsVectorLayer *vectorLayer = qobject_cast< QgsVectorLayer * >( layer ) )
2929 {
2930 if ( const QgsFeatureRenderer *renderer = vectorLayer->renderer() )
2931 {
2932 const double rendererRefreshRate = QgsSymbolLayerUtils::rendererFrameRate( renderer );
2933 if ( rendererRefreshRate > 0 )
2934 {
2935 layerRefreshInterval = 1000 / rendererRefreshRate;
2936 }
2937 }
2938 }
2939
2940 if ( layerRefreshInterval == 0 )
2941 continue;
2942
2943 minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layerRefreshInterval, minAutoRefreshInterval ) : layerRefreshInterval;
2944 }
2945
2946 if ( minAutoRefreshInterval > 0 )
2947 {
2948 mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
2949 mAutoRefreshTimer.start();
2950 }
2951 else
2952 {
2953 mAutoRefreshTimer.stop();
2954 }
2955}
2956
2957void QgsMapCanvas::projectThemesChanged()
2958{
2959 if ( mTheme.isEmpty() )
2960 return;
2961
2962 if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
2963 {
2964 // theme has been removed - stop following
2965 setTheme( QString() );
2966 }
2967
2968}
2969
2971{
2972 return mMapTool;
2973}
2974
2976{
2977 return mProject;
2978}
2979
2980void QgsMapCanvas::panActionEnd( QPoint releasePoint )
2981{
2982 // move map image and other items to standard position
2983 moveCanvasContents( true ); // true means reset
2984
2985 // use start and end box points to calculate the extent
2987 QgsPointXY end = getCoordinateTransform()->toMapCoordinates( releasePoint );
2988
2989 // modify the center
2990 double dx = end.x() - start.x();
2991 double dy = end.y() - start.y();
2992 QgsPointXY c = center();
2993 c.set( c.x() - dx, c.y() - dy );
2994 setCenter( c );
2995
2996 refresh();
2997}
2998
2999void QgsMapCanvas::panActionStart( QPoint releasePoint )
3000{
3001 mCanvasProperties->rubberStartPoint = releasePoint;
3002
3003 mDa = QgsDistanceArea();
3004 mDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
3005 mDa.setSourceCrs( mapSettings().destinationCrs(), QgsProject::instance()->transformContext() );
3006}
3007
3008void QgsMapCanvas::panAction( QMouseEvent *e )
3009{
3010 Q_UNUSED( e )
3011
3012 QgsPointXY currentMapPoint = getCoordinateTransform()->toMapCoordinates( e->pos() );
3013 QgsPointXY startMapPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
3014 try
3015 {
3016 emit panDistanceBearingChanged( mDa.measureLine( currentMapPoint, startMapPoint ), mDa.lengthUnits(), mDa.bearing( currentMapPoint, startMapPoint ) * 180 / M_PI );
3017 }
3018 catch ( QgsCsException & )
3019 {}
3020
3021 // move all map canvas items
3023}
3024
3026{
3027 QPoint pnt( 0, 0 );
3028 if ( !reset )
3029 pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
3030
3031 setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
3032}
3033
3034void QgsMapCanvas::dropEvent( QDropEvent *event )
3035{
3036 if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
3037 {
3039 bool allHandled = true;
3040 for ( const QgsMimeDataUtils::Uri &uri : lst )
3041 {
3042 bool handled = false;
3043 for ( QgsCustomDropHandler *handler : std::as_const( mDropHandlers ) )
3044 {
3045 if ( handler && handler->customUriProviderKey() == uri.providerKey )
3046 {
3047 if ( handler->handleCustomUriCanvasDrop( uri, this ) )
3048 {
3049 handled = true;
3050 break;
3051 }
3052 }
3053 }
3054 if ( !handled )
3055 allHandled = false;
3056 }
3057 if ( allHandled )
3058 event->accept();
3059 else
3060 event->ignore();
3061 }
3062 else
3063 {
3064 event->ignore();
3065 }
3066}
3067
3068void QgsMapCanvas::showEvent( QShowEvent *event )
3069{
3070 Q_UNUSED( event )
3071 updateDevicePixelFromScreen();
3072 // keep device pixel ratio up to date on screen or resolution change
3073 QWindow *l_window = window()->windowHandle();
3074 if ( l_window )
3075 {
3076 connect( l_window, &QWindow::screenChanged, this, [ = ]( QScreen * )
3077 {
3078 disconnect( mScreenDpiChangedConnection );
3079 QWindow *windowInLambda = window()->windowHandle();
3080 if ( windowInLambda )
3081 {
3082 mScreenDpiChangedConnection = connect( windowInLambda->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
3083 updateDevicePixelFromScreen();
3084 }
3085 } );
3086
3087 mScreenDpiChangedConnection = connect( l_window->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
3088 }
3089}
3090
3092{
3093 return mCanvasProperties->mouseLastXY;
3094}
3095
3096void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
3097{
3098 if ( !mPreviewEffect )
3099 {
3100 return;
3101 }
3102
3103 mPreviewEffect->setEnabled( previewEnabled );
3104}
3105
3107{
3108 if ( !mPreviewEffect )
3109 {
3110 return false;
3111 }
3112
3113 return mPreviewEffect->isEnabled();
3114}
3115
3117{
3118 if ( !mPreviewEffect )
3119 {
3120 return;
3121 }
3122
3123 mPreviewEffect->setMode( mode );
3124}
3125
3127{
3128 if ( !mPreviewEffect )
3129 {
3131 }
3132
3133 return mPreviewEffect->mode();
3134}
3135
3137{
3138 if ( !mSnappingUtils )
3139 {
3140 // associate a dummy instance, but better than null pointer
3141 QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
3142 c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
3143 }
3144 return mSnappingUtils;
3145}
3146
3148{
3149 mSnappingUtils = utils;
3150}
3151
3152void QgsMapCanvas::readProject( const QDomDocument &doc )
3153{
3154 QgsProject *project = qobject_cast< QgsProject * >( sender() );
3155
3156 QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
3157 if ( nodes.count() )
3158 {
3159 QDomNode node = nodes.item( 0 );
3160
3161 // Search the specific MapCanvas node using the name
3162 if ( nodes.count() > 1 )
3163 {
3164 for ( int i = 0; i < nodes.size(); ++i )
3165 {
3166 QDomElement elementNode = nodes.at( i ).toElement();
3167
3168 if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
3169 {
3170 node = nodes.at( i );
3171 break;
3172 }
3173 }
3174 }
3175
3176 QgsMapSettings tmpSettings;
3177 tmpSettings.readXml( node );
3178 if ( objectName() != QLatin1String( "theMapCanvas" ) )
3179 {
3180 // never manually set the crs for the main canvas - this is instead connected to the project CRS
3181 setDestinationCrs( tmpSettings.destinationCrs() );
3182 }
3183 setExtent( tmpSettings.extent() );
3184 setRotation( tmpSettings.rotation() );
3186
3187 clearExtentHistory(); // clear the extent history on project load
3188
3189 QDomElement elem = node.toElement();
3190 if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
3191 {
3192 if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
3193 {
3194 setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
3195 }
3196 }
3197 setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
3198
3199 // restore canvas expression context
3200 const QDomNodeList scopeElements = elem.elementsByTagName( QStringLiteral( "expressionContextScope" ) );
3201 if ( scopeElements.size() > 0 )
3202 {
3203 const QDomElement scopeElement = scopeElements.at( 0 ).toElement();
3204 mExpressionContextScope.readXml( scopeElement, QgsReadWriteContext() );
3205 }
3206 }
3207 else
3208 {
3209 QgsDebugMsg( QStringLiteral( "Couldn't read mapcanvas information from project" ) );
3211 {
3213 clearExtentHistory(); // clear the extent history on project load
3214 }
3215 }
3216}
3217
3218void QgsMapCanvas::writeProject( QDomDocument &doc )
3219{
3220 // create node "mapcanvas" and call mMapRenderer->writeXml()
3221
3222 QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
3223 if ( !nl.count() )
3224 {
3225 QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
3226 return;
3227 }
3228 QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
3229
3230 QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
3231 mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
3232 if ( !mTheme.isEmpty() )
3233 mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
3234 mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
3235 qgisNode.appendChild( mapcanvasNode );
3236
3237 mSettings.writeXml( mapcanvasNode, doc );
3238
3239 // store canvas expression context
3240 QDomElement scopeElement = doc.createElement( QStringLiteral( "expressionContextScope" ) );
3241 QgsExpressionContextScope tmpScope( mExpressionContextScope );
3242 tmpScope.removeVariable( QStringLiteral( "atlas_featurenumber" ) );
3243 tmpScope.removeVariable( QStringLiteral( "atlas_pagename" ) );
3244 tmpScope.removeVariable( QStringLiteral( "atlas_feature" ) );
3245 tmpScope.removeVariable( QStringLiteral( "atlas_featureid" ) );
3246 tmpScope.removeVariable( QStringLiteral( "atlas_geometry" ) );
3247 tmpScope.writeXml( scopeElement, doc, QgsReadWriteContext() );
3248 mapcanvasNode.appendChild( scopeElement );
3249
3250 // TODO: store only units, extent, projections, dest CRS
3251}
3252
3253void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center, bool ignoreScaleLock )
3254{
3255 if ( mScaleLocked && !ignoreScaleLock )
3256 {
3257 ScaleRestorer restorer( this );
3259 }
3260 else
3261 {
3263 r.scale( scaleFactor, center );
3264 setExtent( r, true );
3265 refresh();
3266 }
3267}
3268
3270{
3271 // Find out which layer it was that sent the signal.
3272 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
3273 if ( layer )
3274 {
3275 emit selectionChanged( layer );
3276 refresh();
3277 }
3278}
3279
3280void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *event )
3281{
3282 // By default graphics view delegates the drag events to graphics items.
3283 // But we do not want that and by ignoring the drag enter we let the
3284 // parent (e.g. QgisApp) to handle drops of map layers etc.
3285
3286 // so we ONLY accept the event if we know in advance that a custom drop handler
3287 // wants it
3288
3289 if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
3290 {
3292 bool allHandled = true;
3293 for ( const QgsMimeDataUtils::Uri &uri : lst )
3294 {
3295 bool handled = false;
3296 for ( QgsCustomDropHandler *handler : std::as_const( mDropHandlers ) )
3297 {
3298 if ( handler->canHandleCustomUriCanvasDrop( uri, this ) )
3299 {
3300 handled = true;
3301 break;
3302 }
3303 }
3304 if ( !handled )
3305 allHandled = false;
3306 }
3307 if ( allHandled )
3308 event->accept();
3309 else
3310 event->ignore();
3311 }
3312 else
3313 {
3314 event->ignore();
3315 }
3316}
3317
3318bool QgsMapCanvas::viewportEvent( QEvent *event )
3319{
3320 if ( event->type() == QEvent::ToolTip && mMapTool && mMapTool->canvasToolTipEvent( qgis::down_cast<QHelpEvent *>( event ) ) )
3321 {
3322 return true;
3323 }
3324 return QGraphicsView::viewportEvent( event );
3325}
3326
3327void QgsMapCanvas::mapToolDestroyed()
3328{
3329 QgsDebugMsgLevel( QStringLiteral( "maptool destroyed" ), 2 );
3330 mMapTool = nullptr;
3331}
3332
3333bool QgsMapCanvas::event( QEvent *e )
3334{
3335 if ( !QTouchDevice::devices().empty() )
3336 {
3337 if ( e->type() == QEvent::Gesture )
3338 {
3339 if ( QTapAndHoldGesture *tapAndHoldGesture = qobject_cast< QTapAndHoldGesture * >( static_cast<QGestureEvent *>( e )->gesture( Qt::TapAndHoldGesture ) ) )
3340 {
3341 QPointF pos = tapAndHoldGesture->position();
3342 pos = mapFromGlobal( QPoint( pos.x(), pos.y() ) );
3343 QgsPointXY mapPoint = getCoordinateTransform()->toMapCoordinates( pos.x(), pos.y() );
3344 emit tapAndHoldGestureOccurred( mapPoint, tapAndHoldGesture );
3345 }
3346
3347 // call handler of current map tool
3348 if ( mMapTool )
3349 {
3350 return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
3351 }
3352 }
3353 }
3354
3355 // pass other events to base class
3356 return QGraphicsView::event( e );
3357}
3358
3360{
3361 // reload all layers in canvas
3362 const QList<QgsMapLayer *> layers = mapSettings().layers();
3363 for ( QgsMapLayer *layer : layers )
3364 {
3365 layer->reload();
3366 }
3367
3369}
3370
3372{
3373 // clear the cache
3374 clearCache();
3375
3376 // and then refresh
3377 refresh();
3378}
3379
3381{
3382 while ( mRefreshScheduled || mJob )
3383 {
3384 QgsApplication::processEvents();
3385 }
3386}
3387
3389{
3390 mSettings.setSegmentationTolerance( tolerance );
3391}
3392
3394{
3395 mSettings.setSegmentationToleranceType( type );
3396}
3397
3398QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
3399{
3400 QList<QgsMapCanvasAnnotationItem *> annotationItemList;
3401 const QList<QGraphicsItem *> items = mScene->items();
3402 for ( QGraphicsItem *gi : items )
3403 {
3404 QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( gi );
3405 if ( aItem )
3406 {
3407 annotationItemList.push_back( aItem );
3408 }
3409 }
3410
3411 return annotationItemList;
3412}
3413
3415{
3416 mAnnotationsVisible = show;
3417 const QList<QgsMapCanvasAnnotationItem *> items = annotationItems();
3418 for ( QgsMapCanvasAnnotationItem *item : items )
3419 {
3420 item->setVisible( show );
3421 }
3422}
3423
3425{
3426 mSettings.setLabelingEngineSettings( settings );
3427}
3428
3430{
3431 return mSettings.labelingEngineSettings();
3432}
3433
3434void QgsMapCanvas::startPreviewJobs()
3435{
3436 stopPreviewJobs(); //just in case still running
3437
3438 //canvas preview jobs aren't compatible with rotation
3439 // TODO fix this
3440 if ( !qgsDoubleNear( mSettings.rotation(), 0.0 ) )
3441 return;
3442
3443 schedulePreviewJob( 0 );
3444}
3445
3446void QgsMapCanvas::startPreviewJob( int number )
3447{
3448 QgsRectangle mapRect = mSettings.visibleExtent();
3449
3450 if ( number == 4 )
3451 number += 1;
3452
3453 int j = number / 3;
3454 int i = number % 3;
3455
3456 //copy settings, only update extent
3457 QgsMapSettings jobSettings = mSettings;
3458
3459 double dx = ( i - 1 ) * mapRect.width();
3460 double dy = ( 1 - j ) * mapRect.height();
3461 QgsRectangle jobExtent = mapRect;
3462
3463 jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
3464 jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
3465 jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
3466 jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
3467
3468 jobSettings.setExtent( jobExtent );
3469 jobSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, false );
3471
3472 // truncate preview layers to fast layers
3473 const QList<QgsMapLayer *> layers = jobSettings.layers();
3474 QList< QgsMapLayer * > previewLayers;
3476 context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
3477 for ( QgsMapLayer *layer : layers )
3478 {
3479 context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
3480 QgsDataProvider *provider = layer->dataProvider();
3481 if ( provider && !provider->renderInPreview( context ) )
3482 {
3483 QgsDebugMsgLevel( QStringLiteral( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
3484 continue;
3485 }
3486
3487 previewLayers << layer;
3488 }
3489 jobSettings.setLayers( previewLayers );
3490
3491 QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
3492 job->setProperty( "number", number );
3493 mPreviewJobs.append( job );
3494 connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
3495 job->start();
3496}
3497
3498void QgsMapCanvas::stopPreviewJobs()
3499{
3500 mPreviewTimer.stop();
3501 for ( auto previewJob = mPreviewJobs.constBegin(); previewJob != mPreviewJobs.constEnd(); ++previewJob )
3502 {
3503 if ( *previewJob )
3504 {
3505 disconnect( *previewJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
3506 connect( *previewJob, &QgsMapRendererQImageJob::finished, *previewJob, &QgsMapRendererQImageJob::deleteLater );
3507 ( *previewJob )->cancelWithoutBlocking();
3508 }
3509 }
3510 mPreviewJobs.clear();
3511}
3512
3513void QgsMapCanvas::schedulePreviewJob( int number )
3514{
3515 mPreviewTimer.setSingleShot( true );
3516 mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
3517 disconnect( mPreviewTimerConnection );
3518 mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
3519 {
3520 startPreviewJob( number );
3521 } );
3522 mPreviewTimer.start();
3523}
3524
3525bool QgsMapCanvas::panOperationInProgress()
3526{
3527 if ( mCanvasProperties->panSelectorDown )
3528 return true;
3529
3530 if ( QgsMapToolPan *panTool = qobject_cast< QgsMapToolPan *>( mMapTool ) )
3531 {
3532 if ( panTool->isDragging() )
3533 return true;
3534 }
3535
3536 return false;
3537}
3538
3539int QgsMapCanvas::nextZoomLevel( const QList<double> &resolutions, bool zoomIn ) const
3540{
3541 int resolutionLevel = -1;
3542 double currentResolution = mapUnitsPerPixel();
3543
3544 for ( int i = 0, n = resolutions.size(); i < n; ++i )
3545 {
3546 if ( qgsDoubleNear( resolutions[i], currentResolution, 0.0001 ) )
3547 {
3548 resolutionLevel = zoomIn ? ( i - 1 ) : ( i + 1 );
3549 break;
3550 }
3551 else if ( currentResolution <= resolutions[i] )
3552 {
3553 resolutionLevel = zoomIn ? ( i - 1 ) : i;
3554 break;
3555 }
3556 }
3557 return ( resolutionLevel < 0 || resolutionLevel >= resolutions.size() ) ? -1 : resolutionLevel;
3558}
3559
3561{
3562 if ( !mZoomResolutions.isEmpty() )
3563 {
3564 int zoomLevel = nextZoomLevel( mZoomResolutions, true );
3565 if ( zoomLevel != -1 )
3566 {
3567 return mZoomResolutions.at( zoomLevel ) / mapUnitsPerPixel();
3568 }
3569 }
3570 return 1 / mWheelZoomFactor;
3571}
3572
3574{
3575 if ( !mZoomResolutions.isEmpty() )
3576 {
3577 int zoomLevel = nextZoomLevel( mZoomResolutions, false );
3578 if ( zoomLevel != -1 )
3579 {
3580 return mZoomResolutions.at( zoomLevel ) / mapUnitsPerPixel();
3581 }
3582 }
3583 return mWheelZoomFactor;
3584}
@ YX
Northing/Easting (or Latitude/Longitude for geographic CRS)
@ View
Renderer used for displaying on screen.
@ BallparkTransformsAreAppropriate
Indicates that approximate "ballpark" results are appropriate for this coordinate transform....
@ IgnoreImpossibleTransformations
Indicates that impossible transformations (such as those which attempt to transform between two diffe...
@ 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.
static Qgis::CoordinateOrder defaultCoordinateOrderForCrs(const QgsCoordinateReferenceSystem &crs)
Returns the default coordinate order to use for the specified crs.
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
This class represents a coordinate reference system (CRS).
@ MediumString
A medium-length string, recommended for general purpose use.
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
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
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:230
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 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 panToSelected(QgsMapLayer *layer=nullptr)
Pan to the selected features of current ayer keeping same extent.
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 zoomToSelected(QgsMapLayer *layer=nullptr)
Zoom to the extent of the selected features of provided map layer.
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.
void selectionChanged(QgsMapLayer *layer)
Emitted when selection in any layer gets changed.
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 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 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 setCustomDropHandlers(const QVector< QPointer< QgsCustomDropHandler > > &handlers)
Sets a list of custom drop handlers to use when drop events occur on the canvas.
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 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...
QgsMapLayerType type
Definition: qgsmaplayer.h:80
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 rendererChanged()
Signal emitted when renderer is changed.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1502
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:1509
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
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.
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
QgsRenderedItemResults * takeRenderedItemResults()
Takes the rendered item results from the map render job and returns them.
virtual bool isActive() const =0
Tell whether the rendering job is currently running in background.
virtual QgsLabelingResults * takeLabelingResults()=0
Gets pointer to internal labeling engine (in order to get access to the results).
void setLayerRenderingTimeHints(const QHash< QString, int > &hints)
Sets approximate render times (in ms) for map layers.
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
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
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 setFrameRate(double rate)
Sets the frame rate of the map (in frames per second), for maps which are part of an animation.
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.
const QgsMapToPixel & mapToPixel() const
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.
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 setCurrentFrame(long long frame)
Sets the current frame of the map, for maps which are part of an animation.
void setPathResolver(const QgsPathResolver &resolver)
Sets the path resolver for conversion between relative and absolute paths during rendering operations...
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:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:478
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:112
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
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:76
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:75
void setStrokeColor(const QColor &color)
Sets the stroke color for the rubberband.
QColor secondaryStrokeColor
Definition: qgsrubberband.h:78
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:283
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.
static double rendererFrameRate(const QgsFeatureRenderer *renderer)
Calculates the frame rate (in frames per second) at which the given renderer must be redrawn.
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.
Implements a temporal controller based on a frame by frame navigation and animation.
@ NavigationOff
Temporal navigation is disabled.
@ FixedRange
Temporal navigation relies on a fixed datetime range.
@ Animated
Temporal navigation relies on frames within a datetime range.
void navigationModeChanged(QgsTemporalNavigationObject::NavigationMode mode)
Emitted whenever the navigation mode changes.
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.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsRectangle boundingBoxOfSelected() const
Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,...
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.
Implements a map layer that is dedicated to rendering of vector tiles.
QList< QgsFeature > selectedFeatures() const
Returns the list of features currently selected in the layer.
void selectionChanged()
Emitted whenever the selected features in the layer are changed.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
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
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
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:2404
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:2330
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.