QGIS API Documentation  2.99.0-Master (314842d)
qgsmapcanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapcanvas.cpp - description
3  -------------------
4 begin : Sun Jun 30 2002
5 copyright : (C) 2002 by Gary E.Sherman
6 email : sherman at mrcc.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 #include <QtGlobal>
20 #include <QApplication>
21 #include <QCursor>
22 #include <QDir>
23 #include <QFile>
24 #include <QGraphicsItem>
25 #include <QGraphicsScene>
26 #include <QGraphicsView>
27 #include <QKeyEvent>
28 #include <QMouseEvent>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QPixmap>
32 #include <QRect>
33 #include <QTextStream>
34 #include <QResizeEvent>
35 #include <QString>
36 #include <QStringList>
37 #include <QWheelEvent>
38 
39 #include "qgis.h"
40 #include "qgssettings.h"
42 #include "qgsapplication.h"
43 #include "qgscsexception.h"
45 #include "qgsfeatureiterator.h"
46 #include "qgslogger.h"
47 #include "qgsmapcanvas.h"
48 #include "qgsmapcanvasmap.h"
50 #include "qgsmaplayer.h"
51 #include "qgsmaptoolpan.h"
52 #include "qgsmaptoolzoom.h"
53 #include "qgsmaptopixel.h"
54 #include "qgsmapoverviewcanvas.h"
55 #include "qgsmaprenderercache.h"
59 #include "qgsmessagelog.h"
60 #include "qgsmessageviewer.h"
61 #include "qgspallabeling.h"
62 #include "qgsproject.h"
63 #include "qgsrubberband.h"
64 #include "qgsvectorlayer.h"
65 #include "qgscursors.h"
66 #include "qgsmapthemecollection.h"
67 #include <cmath>
68 
69 
74 //TODO QGIS 3.0 - remove
76 {
77  public:
79  : mouseButtonDown( false )
80  , panSelectorDown( false )
81  { }
82 
85 
87  QPoint mouseLastXY;
88 
91 
94 };
95 
96 
97 
98 QgsMapCanvas::QgsMapCanvas( QWidget *parent )
99  : QGraphicsView( parent )
101  , mMap( nullptr )
102  , mFrozen( false )
103  , mRefreshScheduled( false )
104  , mRenderFlag( true ) // by default, the canvas is rendered
105  , mCurrentLayer( nullptr )
106  , mScene( nullptr )
107  , mMapTool( nullptr )
108  , mLastNonZoomMapTool( nullptr )
109  , mLastExtentIndex( -1 )
110  , mWheelZoomFactor( 2.0 )
111  , mJob( nullptr )
112  , mJobCanceled( false )
113  , mLabelingResults( nullptr )
114  , mUseParallelRendering( false )
115  , mDrawRenderingStats( false )
116  , mCache( nullptr )
117  , mResizeTimer( nullptr )
118  , mPreviewEffect( nullptr )
119  , mSnappingUtils( nullptr )
120  , mScaleLocked( false )
121  , mExpressionContextScope( tr( "Map Canvas" ) )
122  , mZoomDragging( false )
123 {
124  mScene = new QGraphicsScene();
125  setScene( mScene );
126  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
127  setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
128  setMouseTracking( true );
129  setFocusPolicy( Qt::StrongFocus );
130 
131  mResizeTimer = new QTimer( this );
132  mResizeTimer->setSingleShot( true );
133  connect( mResizeTimer, &QTimer::timeout, this, &QgsMapCanvas::refresh );
134 
135  // create map canvas item which will show the map
136  mMap = new QgsMapCanvasMap( this );
137 
138  // project handling
140  this, &QgsMapCanvas::readProject );
143 
144  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );
145  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsMapCanvas::projectThemesChanged );
146 
150 
151  //segmentation parameters
152  QgsSettings settings;
153  double segmentationTolerance = settings.value( QStringLiteral( "qgis/segmentationTolerance" ), "0.01745" ).toDouble();
154  QgsAbstractGeometry::SegmentationToleranceType toleranceType = QgsAbstractGeometry::SegmentationToleranceType( settings.value( QStringLiteral( "qgis/segmentationToleranceType" ), 0 ).toInt() );
155  mSettings.setSegmentationTolerance( segmentationTolerance );
156  mSettings.setSegmentationToleranceType( toleranceType );
157 
158  mWheelZoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
159 
160  QSize s = viewport()->size();
161  mSettings.setOutputSize( s );
162  setSceneRect( 0, 0, s.width(), s.height() );
163  mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );
164 
165  moveCanvasContents( true );
166 
167  connect( &mMapUpdateTimer, SIGNAL( timeout() ), SLOT( mapUpdateTimeout() ) );
168  mMapUpdateTimer.setInterval( 250 );
169 
170 #ifdef Q_OS_WIN
171  // Enable touch event on Windows.
172  // Qt on Windows needs to be told it can take touch events or else it ignores them.
173  grabGesture( Qt::PinchGesture );
174  viewport()->setAttribute( Qt::WA_AcceptTouchEvents );
175 #endif
176 
177  mPreviewEffect = new QgsPreviewEffect( this );
178  viewport()->setGraphicsEffect( mPreviewEffect );
179 
180  QPixmap zoomPixmap = QPixmap( ( const char ** )( zoom_in ) );
181  mZoomCursor = QCursor( zoomPixmap, 7, 7 );
182 
183  connect( &mAutoRefreshTimer, &QTimer::timeout, this, &QgsMapCanvas::autoRefreshTriggered );
184 
185  setInteractive( false );
186 
187  refresh();
188 
189 } // QgsMapCanvas ctor
190 
191 
193 {
194  if ( mMapTool )
195  {
196  mMapTool->deactivate();
197  mMapTool = nullptr;
198  }
199  mLastNonZoomMapTool = nullptr;
200 
201  // delete canvas items prior to deleting the canvas
202  // because they might try to update canvas when it's
203  // already being destructed, ends with segfault
204  QList<QGraphicsItem *> list = mScene->items();
205  QList<QGraphicsItem *>::iterator it = list.begin();
206  while ( it != list.end() )
207  {
208  QGraphicsItem *item = *it;
209  delete item;
210  ++it;
211  }
212 
213  mScene->deleteLater(); // crashes in python tests on windows
214 
215  // mCanvasProperties auto-deleted via QScopedPointer
216  // CanvasProperties struct has its own dtor for freeing resources
217 
218  if ( mJob )
219  {
220  whileBlocking( mJob )->cancel();
221  delete mJob;
222  }
223 
224  delete mCache;
225 
226  delete mLabelingResults;
227 
228 } // dtor
229 
231 {
232  // do not go higher or lower than min max magnification ratio
233  double magnifierMin = QgisGui::CANVAS_MAGNIFICATION_MIN;
234  double magnifierMax = QgisGui::CANVAS_MAGNIFICATION_MAX;
235  factor = qBound( magnifierMin, factor, magnifierMax );
236 
237  // the magnifier widget is in integer percent
238  if ( !qgsDoubleNear( factor, mSettings.magnificationFactor(), 0.01 ) )
239  {
240  mSettings.setMagnificationFactor( factor );
241  refresh();
242  emit magnificationChanged( factor );
243  }
244 }
245 
247 {
248  return mSettings.magnificationFactor();
249 }
250 
252 {
253  mSettings.setFlag( QgsMapSettings::Antialiasing, flag );
254 } // anti aliasing
255 
257 {
258  mSettings.setFlag( QgsMapSettings::RenderMapTile, flag );
259 }
260 
262 {
263  QList<QgsMapLayer *> layers = mapSettings().layers();
264  if ( index >= 0 && index < ( int ) layers.size() )
265  return layers[index];
266  else
267  return nullptr;
268 }
269 
270 
272 {
273  mCurrentLayer = layer;
274  emit currentLayerChanged( layer );
275 }
276 
278 {
279  return mapSettings().scale();
280 } // scale
281 
283 {
284  return nullptr != mJob;
285 } // isDrawing
286 
287 // return the current coordinate transform based on the extents and
288 // device size
290 {
291  return &mapSettings().mapToPixel();
292 }
293 
294 void QgsMapCanvas::setLayers( const QList<QgsMapLayer *> &layers )
295 {
296  // following a theme => request denied!
297  if ( !mTheme.isEmpty() )
298  return;
299 
300  setLayersPrivate( layers );
301 }
302 
303 void QgsMapCanvas::setLayersPrivate( const QList<QgsMapLayer *> &layers )
304 {
305  QList<QgsMapLayer *> oldLayers = mSettings.layers();
306 
307  // update only if needed
308  if ( layers == oldLayers )
309  return;
310 
311  Q_FOREACH ( QgsMapLayer *layer, oldLayers )
312  {
313  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
314  disconnect( layer, &QgsMapLayer::crsChanged, this, &QgsMapCanvas::layerCrsChange );
315  disconnect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
316  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
317  {
319  }
320  }
321 
322  mSettings.setLayers( layers );
323 
324  Q_FOREACH ( QgsMapLayer *layer, layers )
325  {
326  if ( !layer )
327  continue;
328  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapCanvas::layerRepaintRequested );
329  connect( layer, &QgsMapLayer::crsChanged, this, &QgsMapCanvas::layerCrsChange );
330  connect( layer, &QgsMapLayer::autoRefreshIntervalChanged, this, &QgsMapCanvas::updateAutoRefreshTimer );
331  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
332  {
334  }
335  }
337 
338  QgsDebugMsg( "Layers have changed, refreshing" );
339  emit layersChanged();
340 
341  updateAutoRefreshTimer();
342  refresh();
343 }
344 
345 
347 {
348  return mSettings;
349 }
350 
352 {
353  if ( mSettings.destinationCrs() == crs )
354  return;
355 
356  // try to reproject current extent to the new one
357  QgsRectangle rect;
358  if ( !mSettings.visibleExtent().isEmpty() )
359  {
360  QgsCoordinateTransform transform( mSettings.destinationCrs(), crs );
361  try
362  {
363  rect = transform.transformBoundingBox( mSettings.visibleExtent() );
364  }
365  catch ( QgsCsException &e )
366  {
367  Q_UNUSED( e );
368  QgsDebugMsg( QString( "Transform error caught: %1" ).arg( e.what() ) );
369  }
370  }
371 
372  if ( !rect.isEmpty() )
373  {
374  setExtent( rect );
375  }
376 
377  mSettings.setDestinationCrs( crs );
378  updateScale();
379 
380  QgsDebugMsg( "refreshing after destination CRS changed" );
381  refresh();
382 
384 
385  emit destinationCrsChanged();
386 }
387 
389 {
390  return mLabelingResults;
391 }
392 
394 {
395  if ( enabled == isCachingEnabled() )
396  return;
397 
398  if ( mJob && mJob->isActive() )
399  {
400  // wait for the current rendering to finish, before touching the cache
401  mJob->waitForFinished();
402  }
403 
404  if ( enabled )
405  {
406  mCache = new QgsMapRendererCache;
407  }
408  else
409  {
410  delete mCache;
411  mCache = nullptr;
412  }
413 }
414 
416 {
417  return nullptr != mCache;
418 }
419 
421 {
422  if ( mCache )
423  mCache->clear();
424 }
425 
427 {
428  mUseParallelRendering = enabled;
429 }
430 
432 {
433  return mUseParallelRendering;
434 }
435 
436 void QgsMapCanvas::setMapUpdateInterval( int timeMilliseconds )
437 {
438  mMapUpdateTimer.setInterval( timeMilliseconds );
439 }
440 
442 {
443  return mMapUpdateTimer.interval();
444 }
445 
446 
448 {
449  return mCurrentLayer;
450 }
451 
452 
454 {
455  if ( !mSettings.hasValidSettings() )
456  {
457  QgsDebugMsg( "CANVAS refresh - invalid settings -> nothing to do" );
458  return;
459  }
460 
461  if ( !mRenderFlag || mFrozen )
462  {
463  QgsDebugMsg( "CANVAS render flag off" );
464  return;
465  }
466 
467  if ( mRefreshScheduled )
468  {
469  QgsDebugMsg( "CANVAS refresh already scheduled" );
470  return;
471  }
472 
473  mRefreshScheduled = true;
474 
475  QgsDebugMsg( "CANVAS refresh scheduling" );
476 
477  // schedule a refresh
478  QTimer::singleShot( 1, this, SLOT( refreshMap() ) );
479 } // refresh
480 
481 void QgsMapCanvas::refreshMap()
482 {
483  Q_ASSERT( mRefreshScheduled );
484 
485  QgsDebugMsgLevel( "CANVAS refresh!", 3 );
486 
487  stopRendering(); // if any...
488 
489  //build the expression context
490  QgsExpressionContext expressionContext;
491  expressionContext << QgsExpressionContextUtils::globalScope()
494  << new QgsExpressionContextScope( mExpressionContextScope );
495 
496  mSettings.setExpressionContext( expressionContext );
497 
498  if ( !mTheme.isEmpty() )
499  {
500  // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this
501  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
502  // current state of the style. If we had stored the style overrides earlier (such as in
503  // mapThemeChanged slot) then this xml could be out of date...
504  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
505  // just return the style name, we can instead set the overrides in mapThemeChanged and not here
506  mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
507  }
508 
509  // create the renderer job
510  Q_ASSERT( !mJob );
511  mJobCanceled = false;
512  if ( mUseParallelRendering )
513  mJob = new QgsMapRendererParallelJob( mSettings );
514  else
515  mJob = new QgsMapRendererSequentialJob( mSettings );
516  connect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
517  mJob->setCache( mCache );
518 
519  QStringList layersForGeometryCache;
520  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
521  {
522  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
523  {
524  if ( vl->isEditable() )
525  layersForGeometryCache << vl->id();
526  }
527  }
528  mJob->setRequestedGeometryCacheForLayers( layersForGeometryCache );
529 
530  mJob->start();
531 
532  // from now on we can accept refresh requests again
533  // this must be reset only after the job has been started, because
534  // some providers (yes, it's you WCS and AMS!) during preparation
535  // do network requests and start an internal event loop, which may
536  // end up calling refresh() and would schedule another refresh,
537  // deleting the one we have just started.
538  mRefreshScheduled = false;
539 
540  mMapUpdateTimer.start();
541 
542  emit renderStarting();
543 }
544 
545 void QgsMapCanvas::mapThemeChanged( const QString &theme )
546 {
547  if ( theme == mTheme )
548  {
549  // set the canvas layers to match the new layers contained in the map theme
550  // NOTE: we do this when the theme layers change and not when we are refreshing the map
551  // as setLayers() sets up necessary connections to handle changes to the layers
552  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
553  // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this
554  // comment) retrieving layer styles from the theme collection gives an XML snapshot of the
555  // current state of the style. If changes were made to the style then this xml
556  // snapshot goes out of sync...
557  // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to
558  // just return the style name, we can instead set the overrides here and not in refreshMap()
559 
560  clearCache();
561  refresh();
562  }
563 }
564 
565 
566 void QgsMapCanvas::rendererJobFinished()
567 {
568  QgsDebugMsg( QString( "CANVAS finish! %1" ).arg( !mJobCanceled ) );
569 
570  mMapUpdateTimer.stop();
571 
572  // TODO: would be better to show the errors in message bar
573  Q_FOREACH ( const QgsMapRendererJob::Error &error, mJob->errors() )
574  {
575  QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) );
576  }
577 
578  if ( !mJobCanceled )
579  {
580  // take labeling results before emitting renderComplete, so labeling map tools
581  // connected to signal work with correct results
582  if ( !mJob->usedCachedLabels() )
583  {
584  delete mLabelingResults;
585  mLabelingResults = mJob->takeLabelingResults();
586  }
587 
588  QImage img = mJob->renderedImage();
589 
590  // emit renderComplete to get our decorations drawn
591  QPainter p( &img );
592  emit renderComplete( &p );
593 
594  QgsSettings settings;
595  if ( settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
596  {
597  QString logMsg = tr( "Canvas refresh: %1 ms" ).arg( mJob->renderingTime() );
598  QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
599  }
600 
601  if ( mDrawRenderingStats )
602  {
603  int w = img.width(), h = img.height();
604  QFont fnt = p.font();
605  fnt.setBold( true );
606  p.setFont( fnt );
607  int lh = p.fontMetrics().height() * 2;
608  QRect r( 0, h - lh, w, lh );
609  p.setPen( Qt::NoPen );
610  p.setBrush( QColor( 0, 0, 0, 110 ) );
611  p.drawRect( r );
612  p.setPen( Qt::white );
613  QString msg = QStringLiteral( "%1 :: %2 ms" ).arg( mUseParallelRendering ? "PARALLEL" : "SEQUENTIAL" ).arg( mJob->renderingTime() );
614  p.drawText( r, msg, QTextOption( Qt::AlignCenter ) );
615  }
616 
617  p.end();
618 
619  mMap->setContent( img, imageRect( img, mSettings ) );
620  }
621 
622  // now we are in a slot called from mJob - do not delete it immediately
623  // so the class is still valid when the execution returns to the class
624  mJob->deleteLater();
625  mJob = nullptr;
626 
627  emit mapCanvasRefreshed();
628 }
629 
630 QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
631 {
632  // This is a hack to pass QgsMapCanvasItem::setRect what it
633  // expects (encoding of position and size of the item)
634  const QgsMapToPixel &m2p = mapSettings.mapToPixel();
635  QgsPoint topLeft = m2p.toMapPoint( 0, 0 );
636  double res = m2p.mapUnitsPerPixel();
637  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
638  return rect;
639 }
640 
641 void QgsMapCanvas::mapUpdateTimeout()
642 {
643  if ( mJob )
644  {
645  const QImage &img = mJob->renderedImage();
646  mMap->setContent( img, imageRect( img, mSettings ) );
647  }
648 }
649 
651 {
652  if ( mJob )
653  {
654  QgsDebugMsg( "CANVAS stop rendering!" );
655  mJobCanceled = true;
656  disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsMapCanvas::rendererJobFinished );
657  connect( mJob, &QgsMapRendererQImageJob::finished, mJob, &QgsMapRendererQImageJob::deleteLater );
658  mJob->cancelWithoutBlocking();
659  mJob = nullptr;
660  }
661 }
662 
663 //the format defaults to "PNG" if not specified
664 void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, const QString &format )
665 {
666  QPainter painter;
667  QImage image;
668 
669  //
670  //check if the optional QPaintDevice was supplied
671  //
672  if ( theQPixmap )
673  {
674  image = theQPixmap->toImage();
675  painter.begin( &image );
676 
677  // render
678  QgsMapRendererCustomPainterJob job( mSettings, &painter );
679  job.start();
680  job.waitForFinished();
681  emit renderComplete( &painter );
682  }
683  else //use the map view
684  {
685  image = mMap->contentImage().copy();
686  painter.begin( &image );
687  }
688 
689  // draw annotations
690  QStyleOptionGraphicsItem option;
691  option.initFrom( this );
692  QGraphicsItem *item = nullptr;
693  QListIterator<QGraphicsItem *> i( items() );
694  i.toBack();
695  while ( i.hasPrevious() )
696  {
697  item = i.previous();
698 
699  if ( !item || dynamic_cast< QgsMapCanvasAnnotationItem * >( item ) )
700  {
701  continue;
702  }
703 
704  painter.save();
705 
706  QPointF itemScenePos = item->scenePos();
707  painter.translate( itemScenePos.x(), itemScenePos.y() );
708 
709  item->paint( &painter, &option );
710 
711  painter.restore();
712  }
713 
714  painter.end();
715  image.save( fileName, format.toLocal8Bit().data() );
716 
717  //create a world file to go with the image...
719  QString myHeader;
720  // note: use 17 places of precision for all numbers output
721  //Pixel XDim
722  myHeader += qgsDoubleToString( mapUnitsPerPixel() ) + "\r\n";
723  //Rotation on y axis - hard coded
724  myHeader += QLatin1String( "0 \r\n" );
725  //Rotation on x axis - hard coded
726  myHeader += QLatin1String( "0 \r\n" );
727  //Pixel YDim - almost always negative - see
728  //http://en.wikipedia.org/wiki/World_file#cite_note-2
729  myHeader += '-' + qgsDoubleToString( mapUnitsPerPixel() ) + "\r\n";
730  //Origin X (center of top left cell)
731  myHeader += qgsDoubleToString( myRect.xMinimum() + ( mapUnitsPerPixel() / 2 ) ) + "\r\n";
732  //Origin Y (center of top left cell)
733  myHeader += qgsDoubleToString( myRect.yMaximum() - ( mapUnitsPerPixel() / 2 ) ) + "\r\n";
734  QFileInfo myInfo = QFileInfo( fileName );
735  // build the world file name
736  QString outputSuffix = myInfo.suffix();
737  QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
738  + outputSuffix.at( 0 ) + outputSuffix.at( myInfo.suffix().size() - 1 ) + 'w';
739  QFile myWorldFile( myWorldFileName );
740  if ( !myWorldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
741  {
742  return;
743  }
744  QTextStream myStream( &myWorldFile );
745  myStream << myHeader;
746 } // saveAsImage
747 
748 
749 
751 {
752  return mapSettings().visibleExtent();
753 } // extent
754 
756 {
757  return mapSettings().fullExtent();
758 } // extent
759 
760 
761 void QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
762 {
763  QgsRectangle current = extent();
764 
765  if ( ( r == current ) && magnified )
766  return;
767 
768  if ( r.isEmpty() )
769  {
770  if ( !mSettings.hasValidSettings() )
771  {
772  // we can't even just move the map center
773  QgsDebugMsg( "Empty extent - ignoring" );
774  return;
775  }
776 
777  // ### QGIS 3: do not allow empty extent - require users to call setCenter() explicitly
778  QgsDebugMsg( "Empty extent - keeping old scale with new center!" );
779  setCenter( r.center() );
780  }
781  else
782  {
783  mSettings.setExtent( r, magnified );
784  }
785  emit extentsChanged();
786  updateScale();
787  if ( mLastExtent.size() > 20 )
788  mLastExtent.removeAt( 0 );
789 
790  //clear all extent items after current index
791  for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
792  {
793  mLastExtent.removeAt( i );
794  }
795 
796  mLastExtent.append( extent() );
797 
798  // adjust history to no more than 20
799  if ( mLastExtent.size() > 20 )
800  {
801  mLastExtent.removeAt( 0 );
802  }
803 
804  // the last item is the current extent
805  mLastExtentIndex = mLastExtent.size() - 1;
806 
807  // update controls' enabled state
808  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
809  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
810  // notify canvas items of change
812 
813 } // setExtent
814 
816 {
818  double x = center.x();
819  double y = center.y();
820  setExtent(
821  QgsRectangle(
822  x - r.width() / 2.0, y - r.height() / 2.0,
823  x + r.width() / 2.0, y + r.height() / 2.0
824  ),
825  true
826  );
827 } // setCenter
828 
830 {
832  return r.center();
833 }
834 
835 
837 {
838  return mapSettings().rotation();
839 } // rotation
840 
841 void QgsMapCanvas::setRotation( double degrees )
842 {
843  double current = rotation();
844 
845  if ( degrees == current )
846  return;
847 
848  mSettings.setRotation( degrees );
849  emit rotationChanged( degrees );
850  emit extentsChanged(); // visible extent changes with rotation
851 
852  // notify canvas items of change (needed?)
854 
855 } // setRotation
856 
857 
859 {
860  emit scaleChanged( mapSettings().scale() );
861 }
862 
863 
865 {
867  // If the full extent is an empty set, don't do the zoom
868  if ( !extent.isEmpty() )
869  {
870  // Add a 5% margin around the full extent
871  extent.scale( 1.05 );
872  setExtent( extent );
873  }
874  refresh();
875 
876 } // zoomToFullExtent
877 
878 
879 
881 {
882  if ( mLastExtentIndex > 0 )
883  {
884  mLastExtentIndex--;
885  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
886  emit extentsChanged();
887  updateScale();
888  refresh();
889  // update controls' enabled state
890  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
891  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
892  // notify canvas items of change
894  }
895 
896 } // zoomToPreviousExtent
897 
899 {
900  if ( mLastExtentIndex < mLastExtent.size() - 1 )
901  {
902  mLastExtentIndex++;
903  mSettings.setExtent( mLastExtent[mLastExtentIndex] );
904  emit extentsChanged();
905  updateScale();
906  refresh();
907  // update controls' enabled state
908  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
909  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
910  // notify canvas items of change
912  }
913 }// zoomToNextExtent
914 
916 {
917  mLastExtent.clear(); // clear the zoom history list
918  mLastExtent.append( extent() ) ; // set the current extent in the list
919  mLastExtentIndex = mLastExtent.size() - 1;
920  // update controls' enabled state
921  emit zoomLastStatusChanged( mLastExtentIndex > 0 );
922  emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
923 }// clearExtentHistory
924 
926 {
927  if ( !layer )
928  {
929  // use current layer by default
930  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
931  }
932 
933  if ( !layer || layer->selectedFeatureCount() == 0 )
934  return;
935 
937  zoomToFeatureExtent( rect );
938 } // zoomToSelected
939 
941 {
942  // no selected features, only one selected point feature
943  //or two point features with the same x- or y-coordinates
944  if ( rect.isEmpty() )
945  {
946  // zoom in
947  QgsPoint c = rect.center();
948  rect = extent();
949  rect.scale( 1.0, &c );
950  }
951  //zoom to an area
952  else
953  {
954  // Expand rect to give a bit of space around the selected
955  // objects so as to keep them clear of the map boundaries
956  // The same 5% should apply to all margins.
957  rect.scale( 1.05 );
958  }
959 
960  setExtent( rect );
961  refresh();
962 }
963 
965 {
966  if ( !layer )
967  {
968  return;
969  }
970 
971  QgsRectangle bbox;
972  QString errorMsg;
973  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
974  {
975  zoomToFeatureExtent( bbox );
976  }
977  else
978  {
979  emit messageEmitted( tr( "Zoom to feature id failed" ), errorMsg, QgsMessageBar::WARNING );
980  }
981 
982 }
983 
985 {
986  if ( !layer )
987  {
988  return;
989  }
990 
991  QgsRectangle bbox;
992  QString errorMsg;
993  if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
994  {
995  setCenter( bbox.center() );
996  refresh();
997  }
998  else
999  {
1000  emit messageEmitted( tr( "Pan to feature id failed" ), errorMsg, QgsMessageBar::WARNING );
1001  }
1002 }
1003 
1004 bool QgsMapCanvas::boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const
1005 {
1006  QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setSubsetOfAttributes( QgsAttributeList() ) );
1007  bbox.setMinimal();
1008  QgsFeature fet;
1009  int featureCount = 0;
1010  errorMsg.clear();
1011 
1012  while ( it.nextFeature( fet ) )
1013  {
1014  QgsGeometry geom = fet.geometry();
1015  if ( geom.isNull() )
1016  {
1017  errorMsg = tr( "Feature does not have a geometry" );
1018  }
1019  else if ( geom.geometry()->isEmpty() )
1020  {
1021  errorMsg = tr( "Feature geometry is empty" );
1022  }
1023  if ( !errorMsg.isEmpty() )
1024  {
1025  return false;
1026  }
1028  bbox.combineExtentWith( r );
1029  featureCount++;
1030  }
1031 
1032  if ( featureCount != ids.count() )
1033  {
1034  errorMsg = tr( "Feature not found" );
1035  return false;
1036  }
1037 
1038  return true;
1039 }
1040 
1042 {
1043  if ( !layer )
1044  {
1045  // use current layer by default
1046  layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1047  }
1048 
1049  if ( !layer || layer->selectedFeatureCount() == 0 )
1050  return;
1051 
1053  if ( !rect.isNull() )
1054  {
1055  setCenter( rect.center() );
1056  refresh();
1057  }
1058  else
1059  {
1060  emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "Geometry is NULL" ), QgsMessageBar::WARNING );
1061  }
1062 } // panToSelected
1063 
1064 void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
1065 {
1066  if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
1067  {
1068  emit keyPressed( e );
1069  return;
1070  }
1071 
1072  if ( ! mCanvasProperties->mouseButtonDown )
1073  {
1074  // Don't want to interfer with mouse events
1075 
1076  QgsRectangle currentExtent = mapSettings().visibleExtent();
1077  double dx = qAbs( currentExtent.width() / 4 );
1078  double dy = qAbs( currentExtent.height() / 4 );
1079 
1080  switch ( e->key() )
1081  {
1082  case Qt::Key_Left:
1083  QgsDebugMsg( "Pan left" );
1084  setCenter( center() - QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1085  refresh();
1086  break;
1087 
1088  case Qt::Key_Right:
1089  QgsDebugMsg( "Pan right" );
1090  setCenter( center() + QgsVector( dx, 0 ).rotateBy( rotation() * M_PI / 180.0 ) );
1091  refresh();
1092  break;
1093 
1094  case Qt::Key_Up:
1095  QgsDebugMsg( "Pan up" );
1096  setCenter( center() + QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1097  refresh();
1098  break;
1099 
1100  case Qt::Key_Down:
1101  QgsDebugMsg( "Pan down" );
1102  setCenter( center() - QgsVector( 0, dy ).rotateBy( rotation() * M_PI / 180.0 ) );
1103  refresh();
1104  break;
1105 
1106 
1107 
1108  case Qt::Key_Space:
1109  QgsDebugMsg( "Pressing pan selector" );
1110 
1111  //mCanvasProperties->dragging = true;
1112  if ( ! e->isAutoRepeat() )
1113  {
1114  QApplication::setOverrideCursor( Qt::ClosedHandCursor );
1115  mCanvasProperties->panSelectorDown = true;
1116  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1117  }
1118  break;
1119 
1120  case Qt::Key_PageUp:
1121  QgsDebugMsg( "Zoom in" );
1122  zoomIn();
1123  break;
1124 
1125  case Qt::Key_PageDown:
1126  QgsDebugMsg( "Zoom out" );
1127  zoomOut();
1128  break;
1129 
1130 #if 0
1131  case Qt::Key_P:
1132  mUseParallelRendering = !mUseParallelRendering;
1133  refresh();
1134  break;
1135 
1136  case Qt::Key_S:
1137  mDrawRenderingStats = !mDrawRenderingStats;
1138  refresh();
1139  break;
1140 #endif
1141 
1142  default:
1143  // Pass it on
1144  if ( mMapTool )
1145  {
1146  mMapTool->keyPressEvent( e );
1147  }
1148  else e->ignore();
1149 
1150  QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
1151  }
1152  }
1153 
1154  emit keyPressed( e );
1155 
1156 } //keyPressEvent()
1157 
1158 void QgsMapCanvas::keyReleaseEvent( QKeyEvent *e )
1159 {
1160  QgsDebugMsg( "keyRelease event" );
1161 
1162  switch ( e->key() )
1163  {
1164  case Qt::Key_Space:
1165  if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
1166  {
1167  QgsDebugMsg( "Releasing pan selector" );
1168  QApplication::restoreOverrideCursor();
1169  mCanvasProperties->panSelectorDown = false;
1170  panActionEnd( mCanvasProperties->mouseLastXY );
1171  }
1172  break;
1173 
1174  default:
1175  // Pass it on
1176  if ( mMapTool )
1177  {
1178  mMapTool->keyReleaseEvent( e );
1179  }
1180  else e->ignore();
1181 
1182  QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
1183  }
1184 
1185  emit keyReleased( e );
1186 
1187 } //keyReleaseEvent()
1188 
1189 
1191 {
1192  // call handler of current map tool
1193  if ( mMapTool )
1194  {
1195  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1196  mMapTool->canvasDoubleClickEvent( me.get() );
1197  }
1198 }// mouseDoubleClickEvent
1199 
1200 
1201 void QgsMapCanvas::beginZoomRect( QPoint pos )
1202 {
1203  mZoomRect.setRect( 0, 0, 0, 0 );
1204  QApplication::setOverrideCursor( mZoomCursor );
1205  mZoomDragging = true;
1206  mZoomRubberBand.reset( new QgsRubberBand( this, QgsWkbTypes::PolygonGeometry ) );
1207  QColor color( Qt::blue );
1208  color.setAlpha( 63 );
1209  mZoomRubberBand->setColor( color );
1210  mZoomRect.setTopLeft( pos );
1211 }
1212 
1213 void QgsMapCanvas::endZoomRect( QPoint pos )
1214 {
1215  mZoomDragging = false;
1216  mZoomRubberBand.reset( nullptr );
1217  QApplication::restoreOverrideCursor();
1218 
1219  // store the rectangle
1220  mZoomRect.setRight( pos.x() );
1221  mZoomRect.setBottom( pos.y() );
1222 
1223  if ( mZoomRect.width() < 5 && mZoomRect.height() < 5 )
1224  {
1225  //probably a mistake - would result in huge zoom!
1226  return;
1227  }
1228 
1229  //account for bottom right -> top left dragging
1230  mZoomRect = mZoomRect.normalized();
1231 
1232  // set center and zoom
1233  const QSize &zoomRectSize = mZoomRect.size();
1234  const QSize &canvasSize = mSettings.outputSize();
1235  double sfx = ( double )zoomRectSize.width() / canvasSize.width();
1236  double sfy = ( double )zoomRectSize.height() / canvasSize.height();
1237  double sf = qMax( sfx, sfy );
1238 
1239  QgsPoint c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );
1240 
1241  zoomByFactor( sf, &c );
1242  refresh();
1243 }
1244 
1245 void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
1246 {
1247  //use middle mouse button for panning, map tools won't receive any events in that case
1248  if ( e->button() == Qt::MidButton )
1249  {
1250  mCanvasProperties->panSelectorDown = true;
1251  mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
1252  }
1253  else
1254  {
1255  // call handler of current map tool
1256  if ( mMapTool )
1257  {
1258  if ( mMapTool->flags() & QgsMapTool::AllowZoomRect && e->button() == Qt::LeftButton
1259  && e->modifiers() & Qt::ShiftModifier )
1260  {
1261  beginZoomRect( e->pos() );
1262  return;
1263  }
1264  else
1265  {
1266  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1267  mMapTool->canvasPressEvent( me.get() );
1268  }
1269  }
1270  }
1271 
1272  if ( mCanvasProperties->panSelectorDown )
1273  {
1274  return;
1275  }
1276 
1277  mCanvasProperties->mouseButtonDown = true;
1278  mCanvasProperties->rubberStartPoint = e->pos();
1279 
1280 } // mousePressEvent
1281 
1282 
1283 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
1284 {
1285  //use middle mouse button for panning, map tools won't receive any events in that case
1286  if ( e->button() == Qt::MidButton )
1287  {
1288  mCanvasProperties->panSelectorDown = false;
1289  panActionEnd( mCanvasProperties->mouseLastXY );
1290  }
1291  else if ( e->button() == Qt::BackButton )
1292  {
1294  return;
1295  }
1296  else if ( e->button() == Qt::ForwardButton )
1297  {
1298  zoomToNextExtent();
1299  return;
1300  }
1301  else
1302  {
1303  if ( mZoomDragging && e->button() == Qt::LeftButton )
1304  {
1305  endZoomRect( e->pos() );
1306  return;
1307  }
1308 
1309  // call handler of current map tool
1310  if ( mMapTool )
1311  {
1312  // right button was pressed in zoom tool? return to previous non zoom tool
1313  if ( e->button() == Qt::RightButton && mMapTool->flags() & QgsMapTool::Transient )
1314  {
1315  QgsDebugMsg( "Right click in map tool zoom or pan, last tool is " +
1316  QString( mLastNonZoomMapTool ? "not null." : "null." ) );
1317 
1318  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
1319 
1320  // change to older non-zoom tool
1321  if ( mLastNonZoomMapTool
1322  && ( !( mLastNonZoomMapTool->flags() & QgsMapTool::EditTool )
1323  || ( vlayer && vlayer->isEditable() ) ) )
1324  {
1325  QgsMapTool *t = mLastNonZoomMapTool;
1326  mLastNonZoomMapTool = nullptr;
1327  setMapTool( t );
1328  }
1329  return;
1330  }
1331  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1332  mMapTool->canvasReleaseEvent( me.get() );
1333  }
1334  }
1335 
1336 
1337  mCanvasProperties->mouseButtonDown = false;
1338 
1339  if ( mCanvasProperties->panSelectorDown )
1340  return;
1341 
1342 } // mouseReleaseEvent
1343 
1344 void QgsMapCanvas::resizeEvent( QResizeEvent *e )
1345 {
1346  QGraphicsView::resizeEvent( e );
1347  mResizeTimer->start( 500 );
1348 
1349  QSize lastSize = viewport()->size();
1350 
1351  mSettings.setOutputSize( lastSize );
1352 
1353  mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
1354 
1355  moveCanvasContents( true );
1356 
1357  // notify canvas items of change
1359 
1360  updateScale();
1361 
1362  //refresh();
1363 
1364  emit extentsChanged();
1365 }
1366 
1367 void QgsMapCanvas::paintEvent( QPaintEvent *e )
1368 {
1369  // no custom event handling anymore
1370 
1371  QGraphicsView::paintEvent( e );
1372 } // paintEvent
1373 
1375 {
1376  QList<QGraphicsItem *> list = mScene->items();
1377  QList<QGraphicsItem *>::iterator it = list.begin();
1378  while ( it != list.end() )
1379  {
1380  QgsMapCanvasItem *item = dynamic_cast<QgsMapCanvasItem *>( *it );
1381 
1382  if ( item )
1383  {
1384  item->updatePosition();
1385  }
1386 
1387  ++it;
1388  }
1389 }
1390 
1391 
1392 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
1393 {
1394  // Zoom the map canvas in response to a mouse wheel event. Moving the
1395  // wheel forward (away) from the user zooms in
1396 
1397  QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
1398 
1399  if ( mMapTool )
1400  {
1401  mMapTool->wheelEvent( e );
1402  if ( e->isAccepted() )
1403  return;
1404  }
1405 
1406  double zoomFactor = mWheelZoomFactor;
1407 
1408  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
1409  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * qAbs( e->angleDelta().y() );
1410 
1411  if ( e->modifiers() & Qt::ControlModifier )
1412  {
1413  //holding ctrl while wheel zooming results in a finer zoom
1414  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1415  }
1416 
1417  double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
1418 
1419  // zoom map to mouse cursor by scaling
1420  QgsPoint oldCenter = center();
1421  QgsPoint mousePos( getCoordinateTransform()->toMapPoint( e->x(), e->y() ) );
1422  QgsPoint newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * signedWheelFactor ),
1423  mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * signedWheelFactor ) );
1424 
1425  zoomByFactor( signedWheelFactor, &newCenter );
1426 }
1427 
1428 void QgsMapCanvas::setWheelFactor( double factor )
1429 {
1430  mWheelZoomFactor = factor;
1431 }
1432 
1434 {
1435  // magnification is alreday handled in zoomByFactor
1436  zoomByFactor( 1 / mWheelZoomFactor );
1437 }
1438 
1440 {
1441  // magnification is alreday handled in zoomByFactor
1442  zoomByFactor( mWheelZoomFactor );
1443 }
1444 
1445 void QgsMapCanvas::zoomScale( double newScale )
1446 {
1447  zoomByFactor( newScale / scale() );
1448 }
1449 
1450 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
1451 {
1452  double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
1453 
1454  if ( mScaleLocked )
1455  {
1457  }
1458  else
1459  {
1460  // transform the mouse pos to map coordinates
1463  r.scale( scaleFactor, &center );
1464  setExtent( r, true );
1465  refresh();
1466  }
1467 }
1468 
1469 void QgsMapCanvas::setScaleLocked( bool isLocked )
1470 {
1471  mScaleLocked = isLocked;
1472 }
1473 
1474 void QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
1475 {
1476  mCanvasProperties->mouseLastXY = e->pos();
1477 
1478  if ( mCanvasProperties->panSelectorDown )
1479  {
1480  panAction( e );
1481  }
1482  else if ( mZoomDragging )
1483  {
1484  mZoomRect.setBottomRight( e->pos() );
1485  mZoomRubberBand->setToCanvasRectangle( mZoomRect );
1486  mZoomRubberBand->show();
1487  }
1488  else
1489  {
1490  // call handler of current map tool
1491  if ( mMapTool )
1492  {
1493  std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
1494  mMapTool->canvasMoveEvent( me.get() );
1495  }
1496  }
1497 
1498  // show x y on status bar
1499  QPoint xy = e->pos();
1501  emit xyCoordinates( coord );
1502 } // mouseMoveEvent
1503 
1504 
1505 
1508 {
1509  if ( !tool )
1510  return;
1511 
1512  if ( mMapTool )
1513  {
1514  disconnect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1515  mMapTool->deactivate();
1516  }
1517 
1518  if ( ( tool->flags() & QgsMapTool::Transient )
1519  && mMapTool && !( mMapTool->flags() & QgsMapTool::Transient ) )
1520  {
1521  // if zoom or pan tool will be active, save old tool
1522  // to bring it back on right click
1523  // (but only if it wasn't also zoom or pan tool)
1524  mLastNonZoomMapTool = mMapTool;
1525  }
1526  else
1527  {
1528  mLastNonZoomMapTool = nullptr;
1529  }
1530 
1531  QgsMapTool *oldTool = mMapTool;
1532 
1533  // set new map tool and activate it
1534  mMapTool = tool;
1535  if ( mMapTool )
1536  {
1537  connect( mMapTool, &QObject::destroyed, this, &QgsMapCanvas::mapToolDestroyed );
1538  mMapTool->activate();
1539  }
1540 
1541  emit mapToolSet( mMapTool, oldTool );
1542 } // setMapTool
1543 
1545 {
1546  if ( mMapTool && mMapTool == tool )
1547  {
1548  mMapTool->deactivate();
1549  mMapTool = nullptr;
1550  emit mapToolSet( nullptr, mMapTool );
1551  setCursor( Qt::ArrowCursor );
1552  }
1553 
1554  if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
1555  {
1556  mLastNonZoomMapTool = nullptr;
1557  }
1558 }
1559 
1560 void QgsMapCanvas::setCanvasColor( const QColor &color )
1561 {
1562  // background of map's pixmap
1563  mSettings.setBackgroundColor( color );
1564 
1565  // background of the QGraphicsView
1566  QBrush bgBrush( color );
1567  setBackgroundBrush( bgBrush );
1568 #if 0
1569  QPalette palette;
1570  palette.setColor( backgroundRole(), color );
1571  setPalette( palette );
1572 #endif
1573 
1574  // background of QGraphicsScene
1575  mScene->setBackgroundBrush( bgBrush );
1576 }
1577 
1579 {
1580  return mScene->backgroundBrush().color();
1581 }
1582 
1583 void QgsMapCanvas::setSelectionColor( const QColor &color )
1584 {
1585  mSettings.setSelectionColor( color );
1586 }
1587 
1589 {
1590  return mapSettings().layers().size();
1591 } // layerCount
1592 
1593 
1594 QList<QgsMapLayer *> QgsMapCanvas::layers() const
1595 {
1596  return mapSettings().layers();
1597 }
1598 
1599 
1601 {
1602  // called when a layer has changed visibility setting
1603 
1604  refresh();
1605 
1606 } // layerStateChange
1607 
1609 {
1610  // called when a layer's CRS has been changed
1611  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
1612  QString destAuthId = mSettings.destinationCrs().authid();
1613  getDatumTransformInfo( layer, layer->crs().authid(), destAuthId );
1614 
1615 } // layerCrsChange
1616 
1617 
1618 void QgsMapCanvas::freeze( bool frozen )
1619 {
1620  mFrozen = frozen;
1621 }
1622 
1624 {
1625  return mFrozen;
1626 }
1627 
1628 
1630 {
1631  return mapSettings().mapUnitsPerPixel();
1632 } // mapUnitsPerPixel
1633 
1635 {
1636  return mapSettings().mapUnits();
1637 }
1638 
1639 QMap<QString, QString> QgsMapCanvas::layerStyleOverrides() const
1640 {
1641  return mSettings.layerStyleOverrides();
1642 }
1643 
1644 void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
1645 {
1646  if ( overrides == mSettings.layerStyleOverrides() )
1647  return;
1648 
1649  mSettings.setLayerStyleOverrides( overrides );
1650  clearCache();
1652 }
1653 
1654 void QgsMapCanvas::setTheme( const QString &theme )
1655 {
1656  if ( mTheme == theme )
1657  return;
1658 
1659  clearCache();
1660  if ( theme.isEmpty() || !QgsProject::instance()->mapThemeCollection()->hasMapTheme( theme ) )
1661  {
1662  mTheme.clear();
1663  mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
1664  setLayers( QgsProject::instance()->mapThemeCollection()->masterVisibleLayers() );
1665  emit themeChanged( QString() );
1666  }
1667  else
1668  {
1669  mTheme = theme;
1670  setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
1671  emit themeChanged( theme );
1672  }
1673 }
1674 
1676 {
1677  mRenderFlag = flag;
1678 
1679  if ( mRenderFlag )
1680  {
1681  refresh();
1682  }
1683  else
1684  stopRendering();
1685 }
1686 
1687 #if 0
1688 void QgsMapCanvas::connectNotify( const char *signal )
1689 {
1690  Q_UNUSED( signal );
1691  QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
1692 } //connectNotify
1693 #endif
1694 
1696 {
1697  QString destAuthId = mSettings.destinationCrs().authid();
1698  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1699  {
1700  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
1701  if ( vl && vl->geometryType() == QgsWkbTypes::NullGeometry )
1702  continue;
1703 
1704  // if there are more options, ask the user which datum transform to use
1705  if ( !mSettings.datumTransformStore().hasEntryForLayer( layer ) )
1706  getDatumTransformInfo( layer, layer->crs().authid(), destAuthId );
1707  }
1708 }
1709 
1710 void QgsMapCanvas::layerRepaintRequested( bool deferred )
1711 {
1712  if ( !deferred )
1713  refresh();
1714 }
1715 
1716 void QgsMapCanvas::autoRefreshTriggered()
1717 {
1718  if ( mJob )
1719  {
1720  // canvas is currently being redrawn, so we skip this auto refresh
1721  // otherwise we could get stuck in the situation where an auto refresh is triggered
1722  // too often to allow the canvas to ever finish rendering
1723  return;
1724  }
1725 
1726  refresh();
1727 }
1728 
1729 void QgsMapCanvas::updateAutoRefreshTimer()
1730 {
1731  // min auto refresh interval stores the smallest interval between layer auto refreshes. We automatically
1732  // trigger a map refresh on this minimum interval
1733  int minAutoRefreshInterval = -1;
1734  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
1735  {
1736  if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 )
1737  minAutoRefreshInterval = minAutoRefreshInterval > 0 ? qMin( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval();
1738  }
1739 
1740  if ( minAutoRefreshInterval > 0 )
1741  {
1742  mAutoRefreshTimer.setInterval( minAutoRefreshInterval );
1743  mAutoRefreshTimer.start();
1744  }
1745  else
1746  {
1747  mAutoRefreshTimer.stop();
1748  }
1749 }
1750 
1751 void QgsMapCanvas::projectThemesChanged()
1752 {
1753  if ( mTheme.isEmpty() )
1754  return;
1755 
1756  if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( mTheme ) )
1757  {
1758  // theme has been removed - stop following
1759  setTheme( QString() );
1760  }
1761 
1762 }
1763 
1765 {
1766  return mMapTool;
1767 }
1768 
1769 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
1770 {
1771  // move map image and other items to standard position
1772  moveCanvasContents( true ); // true means reset
1773 
1774  // use start and end box points to calculate the extent
1775  QgsPoint start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
1776  QgsPoint end = getCoordinateTransform()->toMapCoordinates( releasePoint );
1777 
1778  // modify the center
1779  double dx = end.x() - start.x();
1780  double dy = end.y() - start.y();
1781  QgsPoint c = center();
1782  c.set( c.x() - dx, c.y() - dy );
1783  setCenter( c );
1784 
1785  refresh();
1786 }
1787 
1788 void QgsMapCanvas::panAction( QMouseEvent *e )
1789 {
1790  Q_UNUSED( e );
1791 
1792  // move all map canvas items
1794 }
1795 
1797 {
1798  QPoint pnt( 0, 0 );
1799  if ( !reset )
1800  pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
1801 
1802  setSceneRect( -pnt.x(), -pnt.y(), viewport()->size().width(), viewport()->size().height() );
1803 }
1804 
1806 {
1807  return mCanvasProperties->mouseLastXY;
1808 }
1809 
1810 void QgsMapCanvas::setPreviewModeEnabled( bool previewEnabled )
1811 {
1812  if ( !mPreviewEffect )
1813  {
1814  return;
1815  }
1816 
1817  mPreviewEffect->setEnabled( previewEnabled );
1818 }
1819 
1821 {
1822  if ( !mPreviewEffect )
1823  {
1824  return false;
1825  }
1826 
1827  return mPreviewEffect->isEnabled();
1828 }
1829 
1831 {
1832  if ( !mPreviewEffect )
1833  {
1834  return;
1835  }
1836 
1837  mPreviewEffect->setMode( mode );
1838 }
1839 
1841 {
1842  if ( !mPreviewEffect )
1843  {
1845  }
1846 
1847  return mPreviewEffect->mode();
1848 }
1849 
1851 {
1852  if ( !mSnappingUtils )
1853  {
1854  // associate a dummy instance, but better than null pointer
1855  QgsMapCanvas *c = const_cast<QgsMapCanvas *>( this );
1856  c->mSnappingUtils = new QgsMapCanvasSnappingUtils( c, c );
1857  }
1858  return mSnappingUtils;
1859 }
1860 
1862 {
1863  mSnappingUtils = utils;
1864 }
1865 
1866 void QgsMapCanvas::readProject( const QDomDocument &doc )
1867 {
1868  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
1869  if ( nodes.count() )
1870  {
1871  QDomNode node = nodes.item( 0 );
1872 
1873  // Search the specific MapCanvas node using the name
1874  if ( nodes.count() > 1 )
1875  {
1876  for ( int i = 0; i < nodes.size(); ++i )
1877  {
1878  QDomElement elementNode = nodes.at( i ).toElement();
1879 
1880  if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
1881  {
1882  node = nodes.at( i );
1883  break;
1884  }
1885  }
1886  }
1887 
1888  QgsMapSettings tmpSettings;
1889  tmpSettings.readXml( node );
1890  if ( objectName() != QStringLiteral( "theMapCanvas" ) )
1891  {
1892  // never manually set the crs for the main canvas - this is instead connected to the project CRS
1893  setDestinationCrs( tmpSettings.destinationCrs() );
1894  }
1895  setExtent( tmpSettings.extent() );
1896  setRotation( tmpSettings.rotation() );
1897  mSettings.datumTransformStore() = tmpSettings.datumTransformStore();
1899 
1900  clearExtentHistory(); // clear the extent history on project load
1901 
1902  QDomElement elem = node.toElement();
1903  if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
1904  {
1905  if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
1906  {
1907  setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
1908  }
1909  }
1910  setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
1911  }
1912  else
1913  {
1914  QgsDebugMsg( "Couldn't read mapcanvas information from project" );
1915  }
1916 }
1917 
1918 void QgsMapCanvas::writeProject( QDomDocument &doc )
1919 {
1920  // create node "mapcanvas" and call mMapRenderer->writeXml()
1921 
1922  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1923  if ( !nl.count() )
1924  {
1925  QgsDebugMsg( "Unable to find qgis element in project file" );
1926  return;
1927  }
1928  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
1929 
1930  QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
1931  mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
1932  if ( !mTheme.isEmpty() )
1933  mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
1934  mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
1935  qgisNode.appendChild( mapcanvasNode );
1936 
1937  mSettings.writeXml( mapcanvasNode, doc );
1938  // TODO: store only units, extent, projections, dest CRS
1939 }
1940 
1942 void QgsMapCanvas::getDatumTransformInfo( const QgsMapLayer *ml, const QString &srcAuthId, const QString &destAuthId )
1943 {
1944  if ( !ml )
1945  {
1946  return;
1947  }
1948 
1949  //check if default datum transformation available
1950  QgsSettings s;
1951  QString settingsString = "/Projections/" + srcAuthId + "//" + destAuthId;
1952  QVariant defaultSrcTransform = s.value( settingsString + "_srcTransform" );
1953  QVariant defaultDestTransform = s.value( settingsString + "_destTransform" );
1954  if ( defaultSrcTransform.isValid() && defaultDestTransform.isValid() )
1955  {
1956  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, defaultSrcTransform.toInt(), defaultDestTransform.toInt() );
1957  return;
1958  }
1959 
1962 
1963  if ( !s.value( QStringLiteral( "/Projections/showDatumTransformDialog" ), false ).toBool() )
1964  {
1965  // just use the default transform
1966  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, -1, -1 );
1967  return;
1968  }
1969 
1970  //get list of datum transforms
1971  QList< QList< int > > dt = QgsCoordinateTransform::datumTransformations( srcCRS, destCRS );
1972  if ( dt.size() < 2 )
1973  {
1974  return;
1975  }
1976 
1977  //if several possibilities: present dialog
1978  QgsDatumTransformDialog d( ml->name(), dt );
1979  d.setDatumTransformInfo( srcCRS.authid(), destCRS.authid() );
1980  if ( d.exec() == QDialog::Accepted )
1981  {
1982  int srcTransform = -1;
1983  int destTransform = -1;
1984  QList<int> t = d.selectedDatumTransform();
1985  if ( !t.isEmpty() )
1986  {
1987  srcTransform = t.at( 0 );
1988  }
1989  if ( t.size() > 1 )
1990  {
1991  destTransform = t.at( 1 );
1992  }
1993  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, srcTransform, destTransform );
1994  if ( d.rememberSelection() )
1995  {
1996  s.setValue( settingsString + "_srcTransform", srcTransform );
1997  s.setValue( settingsString + "_destTransform", destTransform );
1998  }
1999  }
2000  else
2001  {
2002  mSettings.datumTransformStore().addEntry( ml->id(), srcAuthId, destAuthId, -1, -1 );
2003  }
2004 }
2005 
2006 void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPoint *center )
2007 {
2008  if ( mScaleLocked )
2009  {
2010  // zoom map to mouse cursor by magnifying
2012  }
2013  else
2014  {
2016  r.scale( scaleFactor, center );
2017  setExtent( r, true );
2018  refresh();
2019  }
2020 }
2021 
2023 {
2024  // Find out which layer it was that sent the signal.
2025  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
2026  emit selectionChanged( layer );
2027  refresh();
2028 }
2029 
2030 void QgsMapCanvas::dragEnterEvent( QDragEnterEvent *e )
2031 {
2032  // By default graphics view delegates the drag events to graphics items.
2033  // But we do not want that and by ignoring the drag enter we let the
2034  // parent (e.g. QgisApp) to handle drops of map layers etc.
2035  e->ignore();
2036 }
2037 
2038 void QgsMapCanvas::mapToolDestroyed()
2039 {
2040  QgsDebugMsg( "maptool destroyed" );
2041  mMapTool = nullptr;
2042 }
2043 
2044 bool QgsMapCanvas::event( QEvent *e )
2045 {
2046  if ( !QTouchDevice::devices().empty() )
2047  {
2048  if ( e->type() == QEvent::Gesture )
2049  {
2050  // call handler of current map tool
2051  if ( mMapTool )
2052  {
2053  return mMapTool->gestureEvent( static_cast<QGestureEvent *>( e ) );
2054  }
2055  }
2056  }
2057 
2058  // pass other events to base class
2059  return QGraphicsView::event( e );
2060 }
2061 
2063 {
2064  // reload all layers in canvas
2065  for ( int i = 0; i < layerCount(); i++ )
2066  {
2067  QgsMapLayer *l = layer( i );
2068  if ( l )
2069  l->reload();
2070  }
2071 
2072  // clear the cache
2073  clearCache();
2074 
2075  // and then refresh
2076  refresh();
2077 }
2078 
2080 {
2081  while ( mRefreshScheduled || mJob )
2082  {
2083  QgsApplication::processEvents();
2084  }
2085 }
2086 
2088 {
2089  mSettings.setSegmentationTolerance( tolerance );
2090 }
2091 
2093 {
2094  mSettings.setSegmentationToleranceType( type );
2095 }
2096 
2097 QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2098 {
2099  QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2100  QList<QGraphicsItem *> itemList = mScene->items();
2101  QList<QGraphicsItem *>::iterator it = itemList.begin();
2102  for ( ; it != itemList.end(); ++it )
2103  {
2104  QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( *it );
2105  if ( aItem )
2106  {
2107  annotationItemList.push_back( aItem );
2108  }
2109  }
2110 
2111  return annotationItemList;
2112 }
2113 
2115 {
2116  mAnnotationsVisible = show;
2117  Q_FOREACH ( QgsMapCanvasAnnotationItem *item, annotationItems() )
2118  {
2119  item->setVisible( show );
2120  }
2121 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void setRequestedGeometryCacheForLayers(const QStringList &layerIds)
Set which vector layers should be cached while rendering.
Wrapper for iterator of features from vector data provider or vector layer.
void updateCanvasItemPositions()
called on resize or changed extent to notify canvas items to change their rectangle ...
int autoRefreshInterval
Definition: qgsmaplayer.h:57
void finished()
emitted when asynchronous rendering is finished (or canceled).
void setParallelRenderingEnabled(bool enabled)
Set whether the layers are rendered in parallel or sequentially.
static unsigned index
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
QPoint mouseLastXY
Last seen point of the mouse.
A rectangle specified with double values.
Definition: qgsrectangle.h:36
Base class for all map layer types.
Definition: qgsmaplayer.h:52
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
double y
Definition: qgspoint.h:42
Job implementation that renders everything sequentially using a custom painter.
std::unique_ptr< CanvasProperties > mCanvasProperties
Handle pattern for implementation object.
Definition: qgsmapcanvas.h:688
virtual bool isEmpty() const
Returns true if the geometry is empty.
void setRotation(double degrees)
Set the rotation of the map canvas in clockwise degrees.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:225
virtual void canvasMoveEvent(QgsMapMouseEvent *e)
Mouse move event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:146
void setRenderFlag(bool flag)
Sets whether a user has disabled canvas renders via the GUI.
int mapUpdateInterval() const
Find out how often map preview should be updated while it is being rendered (in milliseconds) ...
QList< QgsMapCanvasAnnotationItem * > annotationItems() const
Returns a list of all annotation items in the canvas.
void zoomToNextExtent()
Zoom to the next extent (view)
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
void zoomWithCenter(int x, int y, bool zoomIn)
Zooms in/out with a given center.
void setCanvasColor(const QColor &_newVal)
Write property of QColor bgColor.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
static QList< QList< int > > datumTransformations(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destinationCrs)
Returns list of datum transformations for the given src and dest CRS.
QList< QgsMapLayer * > layers() const
Return the list of layers shown within the map canvas.
const char * zoom_in[]
Bitmap cursors for map operations.
Definition: qgscursors.cpp:21
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry)...
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the stored overrides of styles for rendering layers.
double magnificationFactor() const
Return the magnification factor.
double rotation() const
Return the rotation of the resulting map image Units are clockwise degrees.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void clearExtentHistory()
This class is a composition of two QSettings instances:
Definition: qgssettings.h:51
bool event(QEvent *e) override
Overridden standard event to be gestures aware.
bool mouseButtonDown
Flag to indicate status of mouse button.
void wheelEvent(QWheelEvent *e) override
Overridden mouse wheel event.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:358
void stopRendering()
stop rendering (if there is any right now)
double rotation() const
Get the current map canvas rotation in clockwise degrees.
QgsPreviewEffect::PreviewMode previewMode() const
Returns the current preview mode for the map canvas.
int selectedFeatureCount() const
The number of features that are selected in this layer.
void keyPressEvent(QKeyEvent *e) override
Overridden key press event.
void zoomToFeatureExtent(QgsRectangle &rect)
Zooms to feature extent.
virtual void reload()
Synchronises with changes in the datasource.
Definition: qgsmaplayer.h:299
Allow zooming by rectangle (by holding shift and dragging) while the tool is active.
Definition: qgsmaptool.h:64
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
void enableAntiAliasing(bool flag)
used to determine if anti-aliasing is enabled or not
void scale(double scaleFactor, const QgsPoint *c=nullptr)
Scale the rectangle around its center point.
An abstract class for items that can be placed on the map canvas.
void setCurrentLayer(QgsMapLayer *layer)
QgsPoint toMapPoint(double x, double y) const
int layerCount() const
return number of layers on the map
const QgsDatumTransformStore & datumTransformStore() const
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
void moveCanvasContents(bool reset=false)
called when panning is in action, reset indicates end of panning
void crsChanged()
Emit a signal that layer&#39;s CRS has been reset.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setAnnotationsVisible(bool visible)
Sets whether annotations are visible in the canvas.
QgsPoint toMapCoordinates(int x, int y) const
void readProject(const QDomDocument &)
called to read map canvas settings from project
bool panSelectorDown
Flag to indicate the pan selector key is held down by user.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
void renderComplete(QPainter *)
Emitted when the canvas has rendered.
Snapping utils instance that is connected to a canvas and updates the configuration (map settings + c...
bool isCachingEnabled() const
Check whether images of rendered layers are curerently being cached.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QgsMapTool * mapTool()
Returns the currently active tool.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
QList< QgsMapLayer * > layers() const
Get list of layers for map rendering The layers are stored in the reverse order of how they are rende...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
void mousePressEvent(QMouseEvent *e) override
Overridden mouse press event.
virtual Flags flags() const
Returns the flags for the map tool.
Definition: qgsmaptool.h:71
void mapThemesChanged()
Emitted when map themes within the collection are changed.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
virtual QImage renderedImage()=0
Get a preview/resulting image.
int renderingTime() const
Find out how long it took to finish the job (in milliseconds)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:72
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:198
void zoomLastStatusChanged(bool)
Emitted when zoom last status changed.
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
virtual void canvasPressEvent(QgsMapMouseEvent *e)
Mouse press event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:156
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
void magnificationChanged(double)
Emitted when the scale of the map changes.
QString what() const
Definition: qgsexception.h:38
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void updateScale()
Emits signal scaleChanged to update scale in main window.
QgsUnitTypes::DistanceUnit mapUnits() const
Get units of map&#39;s geographical coordinates - used for scale calculation.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
void resizeEvent(QResizeEvent *e) override
Overridden resize event.
void setDatumTransformInfo(const QString &srcCRSauthId, const QString &destCRSauthId)
Deprecated to be deleted, stuff from here should be moved elsewhere.
void setMapTool(QgsMapTool *mapTool)
Sets the map tool currently being used on the canvas.
void enableMapTileRendering(bool flag)
sets map tile rendering flag
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:84
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
void setSnappingUtils(QgsSnappingUtils *utils)
Assign an instance of snapping utils to the map canvas.
virtual void keyReleaseEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:176
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
QgsRectangle boundingBoxOfSelected() const
Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,0,0,0) is returned.
QgsRectangle extent() const
Return geographical coordinates of the rectangle that should be rendered.
Map tool is an edit tool, which can only be used when layer is editable.
Definition: qgsmaptool.h:63
void setOutputSize(QSize size)
Set the size of the resulting map image.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
virtual void start() override
Start the rendering job and immediately return.
void saveAsImage(const QString &fileName, QPixmap *QPixmap=nullptr, const QString &="PNG")
Save the convtents of the map canvas to disk as an image.
QgsMapThemeCollection mapThemeCollection
Definition: qgsproject.h:83
void addEntry(const QString &layerId, const QString &srcAuthId, const QString &destAuthId, int srcDatumTransform, int destDatumTransform)
void mapCanvasRefreshed()
Emitted when canvas finished a refresh request.
double scale()
Get the last reported scale of the canvas.
void rotationChanged(double)
Emitted when the rotation of the map changes.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
void setMagnificationFactor(double factor)
Set the magnification factor.
void zoomNextStatusChanged(bool)
Emitted when zoom next status changed.
bool isEmpty() const
test if rectangle is empty.
void setRotation(double degrees)
Set the rotation of the resulting map image Units are clockwise degrees.
void clearCache()
Make sure to remove any rendered images from cache (does nothing if cache is not enabled) ...
QgsMapCanvas(QWidget *parent=nullptr)
Constructor.
void layerCrsChange()
This slot is connected to the layer&#39;s CRS change.
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:33
double scale() const
Return the calculated scale of the map.
Job implementation that renders all layers in parallel.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect)
This signal is emitted when selection was changed.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:215
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:186
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
void setSegmentationTolerance(double tolerance)
Sets the segmentation tolerance applied when rendering curved geometries.
void setMapUpdateInterval(int timeMilliseconds)
Set how often map preview should be updated while it is being rendered (in milliseconds) ...
void keyReleased(QKeyEvent *e)
Emit key release event.
virtual void waitForFinished()=0
Block until the job has finished.
double mapUnitsPerPixel() const
Return the distance in geographical coordinates that equals to one pixel in the map.
void readProject(const QDomDocument &)
emitted when project is being read
Enable anti-aliasing for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void getDatumTransformInfo(const QgsMapLayer *ml, const QString &srcAuthId, const QString &destAuthId)
ask user about datum transformation
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Centers canvas extent to feature ids.
#define M_PI
void mouseDoubleClickEvent(QMouseEvent *e) override
Overridden mouse double click event.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void panToSelected(QgsVectorLayer *layer=nullptr)
Pan to the selected features of current (vector) layer keeping same extent.
QList< int > QgsAttributeList
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
void setWheelFactor(double factor)
set wheel zoom factor (should be greater than 1)
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
void messageEmitted(const QString &title, const QString &message, QgsMessageBar::MessageLevel=QgsMessageBar::INFO)
emit a message (usually to be displayed in a message bar)
void destinationCrsChanged()
Emitted when map CRS has changed.
void updateDatumTransformEntries()
Make sure the datum transform store is properly populated.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
bool isFrozen() const
Returns true if canvas is frozen.
void setCachingEnabled(bool enabled)
Set whether to cache images of rendered layers.
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:100
Single scope for storing variables and functions for use within a QgsExpressionContext.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
double mapUnitsPerPixel() const
Return current map units per pixel.
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets a preview mode for the map canvas.
virtual void wheelEvent(QWheelEvent *e)
Mouse wheel event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:166
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspoint.h:109
void setMode(PreviewMode mode)
Sets the mode for the preview effect, which controls how the effect modifies a widgets appearance...
void renderStarting()
Emitted when the canvas is about to be rendered.
A class to represent a point.
Definition: qgspoint.h:37
const QgsMapToPixel & mapToPixel() const
void keyPressed(QKeyEvent *e)
Emit key press event.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
constexpr double CANVAS_MAGNIFICATION_MAX
Maximum magnification level allowed in map canvases.
Definition: qgisgui.h:66
void zoomOut()
Zoom out with fixed factor.
Enable drawing of vertex markers for layers in editing mode.
void waitWhileRendering()
Blocks until the rendering job has finished.
void zoomToPreviousExtent()
Zoom to the previous extent (view)
void xyCoordinates(const QgsPoint &p)
Emits current mouse position.
bool isDrawing()
Find out whether rendering is in progress.
void zoomToSelected(QgsVectorLayer *layer=nullptr)
Zoom to the extent of the selected features of current (vector) layer.
A class to represent a vector.
Definition: qgsvector.h:26
PreviewMode mode() const
Returns the mode used for the preview effect.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setPreviewModeEnabled(bool previewEnabled)
Enables a preview mode for the map canvas.
virtual void start()=0
Start the rendering job and immediately return.
QPoint mouseLastXY()
returns last position of mouse cursor
double magnificationFactor() const
Returns the magnification factor.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QMap< QString, QString > layerStyleOverrides() const
Returns the stored overrides of styles for layers.
void mouseMoveEvent(QMouseEvent *e) override
Overridden mouse move event.
void keyReleaseEvent(QKeyEvent *e) override
Overridden key release event.
void selectionChanged(QgsMapLayer *layer)
Emitted when selection in any layer gets changed.
virtual void keyPressEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:171
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:42
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
expand the rectangle so that covers both the original rectangle and the given rectangle ...
QColor canvasColor() const
Read property of QColor bgColor.
void clear()
Invalidates the cache contents, clearing all cached images.
Abstract base class for all map tools.
Definition: qgsmaptool.h:49
void selectionChangedSlot()
Receives signal about selection change, and pass it on with layer info.
Draw map such that there are no problems between adjacent tiles.
Job implementation that renders everything sequentially in one thread.
QgsUnitTypes::DistanceUnit mapUnits() const
Convience function for returning the current canvas map units.
void setBackgroundColor(const QColor &color)
Set the background color of the map.
void mouseReleaseEvent(QMouseEvent *e) override
Overridden mouse release event.
void writeProject(QDomDocument &)
emitted when project is being written
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:178
QMap< QString, QString > layerStyleOverrides() const
Get map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
QString theme() const
Returns the map&#39;s theme shown in the canvas, if set.
Definition: qgsmapcanvas.h:337
virtual bool gestureEvent(QGestureEvent *e)
gesture event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:181
void setSelectionColor(const QColor &color)
Set color that is used for drawing of selected vector features.
void layerStyleOverridesChanged()
Emitted when the configuration of overridden layer styles changes.
virtual void canvasDoubleClickEvent(QgsMapMouseEvent *e)
Mouse double click event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:151
bool hasEntryForLayer(QgsMapLayer *layer) const
void dragEnterEvent(QDragEnterEvent *e) override
Overridden drag enter event.
void writeProject(QDomDocument &)
called to write map canvas settings to project
void panAction(QMouseEvent *event)
Called when mouse is moving and pan is activated.
void setLayers(const QList< QgsMapLayer *> &layers)
Sets the list of layers that should be shown in the canvas.
QgsRectangle fullExtent() const
returns current extent of layer set
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:359
void zoomToFullExtent()
Zoom to the full extent of all layers.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
QgsRectangle fullExtent() const
Returns the combined extent for all layers on the map canvas.
This class has all the configuration of snapping and can return answers to snapping queries...
const QgsLabelingResults * labelingResults() const
Get access to the labeling results (may be null)
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
void refreshAllLayers()
Reload all layers, clear the cache and refresh the canvas.
void zoomScale(double scale)
Zoom to a specific scale.
Class for doing transforms between two map coordinate systems.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=Section::NoSection) const
Returns the value for setting key.
void setExtent(const QgsRectangle &r, bool magnified=false)
Set the extent of the map canvas.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:200
void setLayers(const QList< QgsMapLayer *> &layers)
Set list of layers for map rendering.
const QgsMapToPixel * getCoordinateTransform()
Get the current coordinate transform.
void scaleChanged(double)
Emitted when the scale of the map changes.
void setSegmentationToleranceType(QgsAbstractGeometry::SegmentationToleranceType type)
Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
void setCenter(const QgsPoint &center)
Set the center of the map canvas, in geographical coordinates.
virtual void cancelWithoutBlocking()=0
Triggers cancelation of the rendering job without blocking.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:205
void setSelectionColor(const QColor &color)
Set color of selected vector features.
QString name
Read property of QString layerName.
Definition: qgsmaplayer.h:56
void paintEvent(QPaintEvent *e) override
Overridden paint event.
void layerStateChange()
This slot is connected to the visibility change of one or more layers.
Enable vector simplification and other rendering optimizations.
void freeze(bool frozen=true)
Freeze/thaw the map canvas.
void setScaleLocked(bool isLocked)
Lock the scale, so zooming can be performed using magnication.
Class that stores computed placement from labeling engine.
This class is responsible for keeping cache of rendered images resulting from a map rendering job...
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Custom exception class for Coordinate Reference System related exceptions.
void setTheme(const QString &theme)
Sets a map theme to show in the canvas.
void readXml(QDomNode &node)
bool nextFeature(QgsFeature &f)
QPoint rubberStartPoint
Beginning point of a rubber band.
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QgsSnappingUtils * snappingUtils() const
Return snapping utility class that is associated with map canvas.
virtual QgsLabelingResults * takeLabelingResults()=0
Get pointer to internal labeling engine (in order to get access to the results).
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void writeXml(QDomNode &node, QDomDocument &doc)
void zoomIn()
Zoom in with fixed factor.
virtual void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
void setValue(const QString &key, const QVariant &value, const Section section=Section::NoSection)
Sets the value of setting key to value.
virtual void updatePosition()
called on changed extent or resize event to update position of the item
bool isParallelRenderingEnabled() const
Check whether the layers are rendered in parallel or sequentially.
void mapToolSet(QgsMapTool *newTool, QgsMapTool *oldTool)
Emit map tool changed with the old tool.
QgsPoint center() const
Get map center, in geographical coordinates.
An interactive map canvas item which displays a QgsAnnotation.
void themeChanged(const QString &theme)
Emitted when the canvas has been assigned a different map theme.
void extentsChanged()
Emitted when the extents of the map change.
QgsAbstractGeometry * geometry() const
Returns the underlying geometry store.
QSize outputSize() const
Return the size of the resulting map image.
QgsMapLayer * layer(int index)
return the map layer at position index in the layer stack
QString authid() const
Returns the authority identifier for the CRS.
virtual bool isActive() const =0
Tell whether the rendering job is currently running in background.
void zoomByFactor(double scaleFactor, const QgsPoint *center=nullptr)
Zoom with the factor supplied.
constexpr double CANVAS_MAGNIFICATION_MIN
Minimum magnification level allowed in map canvases.
Definition: qgisgui.h:58
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:220
void setMagnificationFactor(double factor)
Sets the factor of magnification to apply to the map canvas.
void panActionEnd(QPoint releasePoint)
Ends pan action and redraws the canvas.
bool previewModeEnabled() const
Returns whether a preview mode is enabled for the map canvas.
void layersChanged()
Emitted when a new set of layers has been received.
virtual bool usedCachedLabels() const =0
Returns true if the render job was able to use a cached labeling solution.
double x
Definition: qgspoint.h:41
virtual void canvasReleaseEvent(QgsMapMouseEvent *e)
Mouse release event for overriding. Default implementation does nothing.
Definition: qgsmaptool.cpp:161