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