QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
qgsmapoverviewcanvas.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapoverviewcanvas.cpp
3  Map canvas subclassed for overview
4  -------------------
5  begin : 09/14/2005
6  copyright : (C) 2005 by Martin Dobias
7  email : won.der at centrum.sk
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #include "qgsmapcanvas.h"
20 #include "qgsmaplayer.h"
21 #include "qgsproject.h"
22 #include "qgsmapoverviewcanvas.h"
24 #include "qgsmaptopixel.h"
25 #include "qgsprojectviewsettings.h"
26 #include "qgslogger.h"
27 
28 #include <QPainter>
29 #include <QPainterPath>
30 #include <QPaintEvent>
31 #include <QResizeEvent>
32 #include <QMouseEvent>
33 #include <limits>
34 
35 
37  : QWidget( parent )
38  , mMapCanvas( mapCanvas )
39 
40 {
41  setAutoFillBackground( true );
42  setObjectName( QStringLiteral( "theOverviewCanvas" ) );
43  mPanningWidget = new QgsPanningWidget( this );
44 
47 
52 
54 }
55 
56 void QgsMapOverviewCanvas::resizeEvent( QResizeEvent *e )
57 {
58  mPixmap = QPixmap();
59 
60  mSettings.setOutputSize( e->size() );
61 
63 
64  refresh();
65 
66  QWidget::resizeEvent( e );
67 }
68 
69 void QgsMapOverviewCanvas::showEvent( QShowEvent *e )
70 {
71  refresh();
72  QWidget::showEvent( e );
73 }
74 
75 void QgsMapOverviewCanvas::paintEvent( QPaintEvent *pe )
76 {
77  QPainter paint( this );
78  QRect rect = pe->rect();
79  QRect sourceRect( std::ceil( pe->rect().left() * mPixmap.devicePixelRatio() ),
80  std::ceil( pe->rect().top() * mPixmap.devicePixelRatio() ),
81  std::ceil( pe->rect().width() * mPixmap.devicePixelRatio() ),
82  std::ceil( pe->rect().height() * mPixmap.devicePixelRatio() ) );
83  if ( !mPixmap.isNull() )
84  {
85  paint.drawPixmap( rect.topLeft(), mPixmap, sourceRect );
86  }
87  else
88  {
89  paint.fillRect( rect, QBrush( mSettings.backgroundColor() ) );
90  }
91 }
92 
93 
95 {
96  if ( !mMapCanvas ) return;
97 
98  const QgsRectangle &extent = mMapCanvas->extent();
99 
100  // show only when valid extent is set
101  if ( extent.isEmpty() || mSettings.visibleExtent().isEmpty() )
102  {
103  mPanningWidget->hide();
104  return;
105  }
106 
107  const QPolygonF &vPoly = mMapCanvas->mapSettings().visiblePolygon();
108  const QgsMapToPixel &cXf = mSettings.mapToPixel();
109  QVector< QPoint > pts;
110  pts.push_back( cXf.transform( QgsPointXY( vPoly[0] ) ).toQPointF().toPoint() );
111  pts.push_back( cXf.transform( QgsPointXY( vPoly[1] ) ).toQPointF().toPoint() );
112  pts.push_back( cXf.transform( QgsPointXY( vPoly[2] ) ).toQPointF().toPoint() );
113  pts.push_back( cXf.transform( QgsPointXY( vPoly[3] ) ).toQPointF().toPoint() );
114  mPanningWidget->setPolygon( QPolygon( pts ) );
115  mPanningWidget->show(); // show if hidden
116 }
117 
118 
120 {
121 // if (mPanningWidget->isHidden())
122 // return;
123 
124  // set offset in panning widget if inside it
125  // for better experience with panning :)
126  if ( mPanningWidget->geometry().contains( e->pos() ) )
127  {
128  mPanningCursorOffset = e->pos() - mPanningWidget->pos();
129  }
130  else
131  {
132  // use center of the panning widget if outside
133  const QSize s = mPanningWidget->size();
134  mPanningCursorOffset = QPoint( s.width() / 2, s.height() / 2 );
135  }
136  updatePanningWidget( e->pos() );
137 }
138 
139 
141 {
142 // if (mPanningWidget->isHidden())
143 // return;
144 
145  if ( e->button() == Qt::LeftButton )
146  {
147  // set new extent
148  const QgsMapToPixel &cXf = mSettings.mapToPixel();
149  const QRect rect = mPanningWidget->geometry();
150 
151  const QgsPointXY center = cXf.toMapCoordinates( rect.center() );
152  mMapCanvas->setCenter( center );
153  mMapCanvas->refresh();
154  }
155 }
156 
157 
158 void QgsMapOverviewCanvas::wheelEvent( QWheelEvent *e )
159 {
160  QgsSettings settings;
161  bool reverseZoom = settings.value( QStringLiteral( "qgis/reverse_wheel_zoom" ), false ).toBool();
162  bool zoomIn = reverseZoom ? e->angleDelta().y() < 0 : e->angleDelta().y() > 0;
163  double zoomFactor = zoomIn ? 1. / mMapCanvas->zoomInFactor() : mMapCanvas->zoomOutFactor();
164 
165  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
166  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
167 
168  if ( e->modifiers() & Qt::ControlModifier )
169  {
170  //holding ctrl while wheel zooming results in a finer zoom
171  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
172  }
173 
174  const double signedWheelFactor = zoomIn ? 1 / zoomFactor : zoomFactor;
175 
176  const QgsMapToPixel &cXf = mSettings.mapToPixel();
177  const QgsPointXY center = cXf.toMapCoordinates( e->position().x(), e->position().y() );
178  updatePanningWidget( QPoint( e->position().x(), e->position().y() ) );
179  mMapCanvas->zoomByFactor( signedWheelFactor, &center );
180 }
181 
183 {
184  // move with panning widget if tracking cursor
185  if ( ( e->buttons() & Qt::LeftButton ) == Qt::LeftButton )
186  {
187  updatePanningWidget( e->pos() );
188  }
189 }
190 
191 
193 {
194 // if (mPanningWidget->isHidden())
195 // return;
196  mPanningWidget->move( pos.x() - mPanningCursorOffset.x(), pos.y() - mPanningCursorOffset.y() );
197 }
198 
200 {
201  if ( !isVisible() )
202  return;
203 
205 
206  if ( !mSettings.hasValidSettings() )
207  {
208  mPixmap = QPixmap();
209  update();
210  return; // makes no sense to render anything
211  }
212 
213  if ( mJob )
214  {
215  QgsDebugMsgLevel( QStringLiteral( "oveview - canceling old" ), 2 );
216  mJob->cancel();
217  QgsDebugMsgLevel( QStringLiteral( "oveview - deleting old" ), 2 );
218  delete mJob; // get rid of previous job (if any)
219  }
220 
221  QgsDebugMsgLevel( QStringLiteral( "oveview - starting new" ), 2 );
222 
223  mSettings.setDevicePixelRatio( static_cast<float>( devicePixelRatioF() ) );
224 
225  // TODO: setup overview mode
228  mJob->start();
229 
231 
232  // schedule repaint
233  update();
234 
235  // update panning widget
236  drawExtentRect();
237 }
238 
240 {
241  QgsDebugMsgLevel( QStringLiteral( "overview - finished" ), 2 );
242  mPixmap = QPixmap::fromImage( mJob->renderedImage() );
243 
244  delete mJob;
245  mJob = nullptr;
246 
247  // schedule repaint
248  update();
249 }
250 
252 {
253  if ( !deferred )
254  refresh();
255 }
256 
257 
258 void QgsMapOverviewCanvas::setBackgroundColor( const QColor &color )
259 {
260  mSettings.setBackgroundColor( color );
261 
262  // set erase color
263  QPalette palette;
264  palette.setColor( backgroundRole(), color );
265  setPalette( palette );
266 }
267 
268 void QgsMapOverviewCanvas::setLayers( const QList<QgsMapLayer *> &layers )
269 {
270  const auto oldLayers = mSettings.layers();
271  for ( QgsMapLayer *ml : oldLayers )
272  {
274  }
275 
277 
278  const auto newLayers = mSettings.layers();
279  for ( QgsMapLayer *ml : newLayers )
280  {
282  }
283 
284  refresh();
285 }
286 
288 {
289  QgsRectangle rect;
290  if ( !QgsProject::instance()->viewSettings()->presetFullExtent().isNull() )
291  {
295  try
296  {
297  rect = ct.transformBoundingBox( extent );
298  }
299  catch ( QgsCsException & )
300  {
301  }
302  }
303 
304  if ( rect.isNull() )
305  {
306  if ( mSettings.hasValidSettings() )
307  rect = mSettings.fullExtent();
308  else
309  rect = mMapCanvas->projectExtent();
310  }
311 
312  // expand a bit to keep features on margin
313  rect.scale( 1.1 );
314 
315  mSettings.setExtent( rect );
316  drawExtentRect();
317 }
318 
320 {
322 }
323 
325 {
327 }
328 
329 QList<QgsMapLayer *> QgsMapOverviewCanvas::layers() const
330 {
331  return mSettings.layers();
332 }
333 
334 
336 
337 QgsPanningWidget::QgsPanningWidget( QWidget *parent )
338  : QWidget( parent )
339 {
340  setObjectName( QStringLiteral( "panningWidget" ) );
341  setMinimumSize( 5, 5 );
342  setAttribute( Qt::WA_NoSystemBackground );
343 }
344 
345 void QgsPanningWidget::setPolygon( const QPolygon &p )
346 {
347  if ( p == mPoly ) return;
348  mPoly = p;
349 
350  //ensure polygon is closed
351  if ( mPoly.at( 0 ) != mPoly.at( mPoly.length() - 1 ) )
352  mPoly.append( mPoly.at( 0 ) );
353 
354  const QRect rect = p.boundingRect() + QMargins( 1, 1, 1, 1 );
355  setGeometry( rect );
356  update();
357 }
358 
359 void QgsPanningWidget::paintEvent( QPaintEvent *pe )
360 {
361  Q_UNUSED( pe )
362 
363  QPainter p;
364 
365  p.begin( this );
366  const QPolygonF t = mPoly.translated( -mPoly.boundingRect().left() + 1, -mPoly.boundingRect().top() + 1 );
367 
368  // drawPolygon causes issues on windows - corners of path may be missing resulting in triangles being drawn
369  // instead of rectangles! (Same cause as #13343)
370  QPainterPath path;
371  path.addPolygon( t );
372 
373  QPen pen;
374  pen.setJoinStyle( Qt::MiterJoin );
375  pen.setColor( Qt::white );
376  pen.setWidth( 3 );
377  p.setPen( pen );
378  p.drawPath( path );
379  pen.setColor( Qt::red );
380  pen.setWidth( 1 );
381  p.setPen( pen );
382  p.drawPath( path );
383 
384  p.end();
385 }
386 
387 
388 
@ DrawLabeling
Enable drawing of labels on top of the map.
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.
Definition: qgsexception.h:67
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:93
void extentsChanged()
Emitted when the extents of the map change.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr, bool ignoreScaleLock=false)
Zoom with the factor supplied.
void canvasColorChanged()
Emitted when canvas background color changes.
double zoomInFactor() const
Returns the zoom in factor.
double zoomOutFactor() const
Returns the zoom in factor.
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 destinationCrsChanged()
Emitted when map CRS has changed.
void transformContextChanged()
Emitted when the canvas transform context is changed.
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.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
QPoint mPanningCursorOffset
position of cursor inside panning widget
void wheelEvent(QWheelEvent *e) override
Overridden mouse release event.
void resizeEvent(QResizeEvent *e) override
Overridden resize event.
void setLayers(const QList< QgsMapLayer * > &layers)
updates layer set for overview
void drawExtentRect()
used for overview canvas to reflect changed extent in main map canvas
void mouseReleaseEvent(QMouseEvent *e) override
Overridden mouse release event.
void mouseMoveEvent(QMouseEvent *e) override
Overridden mouse move event.
void transformContextChanged()
Called when the canvas transform context is changed.
void refresh()
renders overview and updates panning widget
void mousePressEvent(QMouseEvent *e) override
Overridden mouse press event.
void layerRepaintRequested(bool deferred=false)
Triggered when a layer in the overview requests a repaint.
void paintEvent(QPaintEvent *pe) override
Overridden paint event.
void setBackgroundColor(const QColor &color)
changes background color
QgsMapOverviewCanvas(QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr)
QgsMapRendererQImageJob * mJob
for rendering overview
void destinationCrsChanged()
Should be called when the canvas destination CRS is changed.
QPixmap mPixmap
pixmap where the map is stored
QgsPanningWidget * mPanningWidget
widget for panning map in overview
void showEvent(QShowEvent *e) override
Overridden show event.
void updatePanningWidget(QPoint pos)
called when panning to reflect mouse movement
QgsMapSettings mSettings
map settings used for rendering of the overview map
QList< QgsMapLayer * > layers() const
Returns list of layers visible in the overview.
QgsMapCanvas * mMapCanvas
main map canvas - used to get/set extent
void finished()
emitted when asynchronous rendering is finished (or canceled).
void start()
Start the rendering job and immediately return.
virtual void cancel()=0
Stop the rendering job - does not return until the job has terminated.
virtual QImage renderedImage()=0
Gets a preview/resulting image.
Job implementation that renders everything sequentially in one thread.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
QPolygonF visiblePolygon() const
Returns the visible area as a polygon (may be rotated)
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setDevicePixelRatio(float dpr)
Sets the device pixel ratio.
QColor backgroundColor() const
Returns the background color of the map.
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
const QgsMapToPixel & mapToPixel() const
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
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...
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.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
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.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:88
A class to represent a 2D point.
Definition: qgspointxy.h:60
void presetFullExtentChanged()
Emitted whenever the presetFullExtent() is changed.
QgsReferencedRectangle fullExtent() const
Returns the full extent of the project, which represents the maximal limits of the project.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:113
const QgsProjectViewSettings * viewSettings() const
Returns the project's view settings, which contains settings and properties relating to how a QgsProj...
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:267
bool isNull() const
Test if the rectangle is null (holding no spatial information).
Definition: qgsrectangle.h:505
bool isEmpty() const
Returns true if the rectangle has no area.
Definition: qgsrectangle.h:492
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsRectangle with associated coordinate reference system.
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.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39