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