QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
27 #include <QPainter>
28 #include <QPainterPath>
29 #include <QPaintEvent>
30 #include <QResizeEvent>
31 #include <QMouseEvent>
32 #include "qgslogger.h"
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 
51 
53 }
54 
55 void QgsMapOverviewCanvas::resizeEvent( QResizeEvent *e )
56 {
57  mPixmap = QPixmap();
58 
59  mSettings.setOutputSize( e->size() );
60 
62 
63  refresh();
64 
65  QWidget::resizeEvent( e );
66 }
67 
68 void QgsMapOverviewCanvas::showEvent( QShowEvent *e )
69 {
70  refresh();
71  QWidget::showEvent( e );
72 }
73 
74 void QgsMapOverviewCanvas::paintEvent( QPaintEvent *pe )
75 {
76  if ( !mPixmap.isNull() )
77  {
78  QPainter paint( this );
79  paint.drawPixmap( pe->rect().topLeft(), mPixmap, pe->rect() );
80  }
81 }
82 
83 
85 {
86  if ( !mMapCanvas ) return;
87 
88  const QgsRectangle &extent = mMapCanvas->extent();
89 
90  // show only when valid extent is set
91  if ( extent.isEmpty() || mSettings.visibleExtent().isEmpty() )
92  {
93  mPanningWidget->hide();
94  return;
95  }
96 
97  const QPolygonF &vPoly = mMapCanvas->mapSettings().visiblePolygon();
98  const QgsMapToPixel &cXf = mSettings.mapToPixel();
99  QVector< QPoint > pts;
100  pts.push_back( cXf.transform( QgsPointXY( vPoly[0] ) ).toQPointF().toPoint() );
101  pts.push_back( cXf.transform( QgsPointXY( vPoly[1] ) ).toQPointF().toPoint() );
102  pts.push_back( cXf.transform( QgsPointXY( vPoly[2] ) ).toQPointF().toPoint() );
103  pts.push_back( cXf.transform( QgsPointXY( vPoly[3] ) ).toQPointF().toPoint() );
104  mPanningWidget->setPolygon( QPolygon( pts ) );
105  mPanningWidget->show(); // show if hidden
106 }
107 
108 
110 {
111 // if (mPanningWidget->isHidden())
112 // return;
113 
114  // set offset in panning widget if inside it
115  // for better experience with panning :)
116  if ( mPanningWidget->geometry().contains( e->pos() ) )
117  {
118  mPanningCursorOffset = e->pos() - mPanningWidget->pos();
119  }
120  else
121  {
122  // use center of the panning widget if outside
123  const QSize s = mPanningWidget->size();
124  mPanningCursorOffset = QPoint( s.width() / 2, s.height() / 2 );
125  }
126  updatePanningWidget( e->pos() );
127 }
128 
129 
131 {
132 // if (mPanningWidget->isHidden())
133 // return;
134 
135  if ( e->button() == Qt::LeftButton )
136  {
137  // set new extent
138  const QgsMapToPixel &cXf = mSettings.mapToPixel();
139  const QRect rect = mPanningWidget->geometry();
140 
141  const QgsPointXY center = cXf.toMapCoordinates( rect.center() );
142  mMapCanvas->setCenter( center );
143  mMapCanvas->refresh();
144  }
145 }
146 
147 
148 void QgsMapOverviewCanvas::wheelEvent( QWheelEvent *e )
149 {
150  double zoomFactor = e->angleDelta().y() > 0 ? 1. / mMapCanvas->zoomInFactor() : mMapCanvas->zoomOutFactor();
151 
152  // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
153  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
154 
155  if ( e->modifiers() & Qt::ControlModifier )
156  {
157  //holding ctrl while wheel zooming results in a finer zoom
158  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
159  }
160 
161  const double signedWheelFactor = e->angleDelta().y() > 0 ? 1 / zoomFactor : zoomFactor;
162 
163  const QgsMapToPixel &cXf = mSettings.mapToPixel();
164 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
165  QgsPointXY center = cXf.toMapCoordinates( e->pos().x(), e->pos().y() );
166  updatePanningWidget( QPoint( e->pos().x(), e->pos().y() ) );
167 #else
168  const QgsPointXY center = cXf.toMapCoordinates( e->position().x(), e->position().y() );
169  updatePanningWidget( QPoint( e->position().x(), e->position().y() ) );
170 #endif
171  mMapCanvas->zoomByFactor( signedWheelFactor, &center );
172 }
173 
175 {
176  // move with panning widget if tracking cursor
177  if ( ( e->buttons() & Qt::LeftButton ) == Qt::LeftButton )
178  {
179  updatePanningWidget( e->pos() );
180  }
181 }
182 
183 
185 {
186 // if (mPanningWidget->isHidden())
187 // return;
188  mPanningWidget->move( pos.x() - mPanningCursorOffset.x(), pos.y() - mPanningCursorOffset.y() );
189 }
190 
192 {
193  if ( !isVisible() )
194  return;
195 
197 
198  if ( !mSettings.hasValidSettings() )
199  {
200  mPixmap = QPixmap();
201  update();
202  return; // makes no sense to render anything
203  }
204 
205  if ( mJob )
206  {
207  QgsDebugMsg( QStringLiteral( "oveview - canceling old" ) );
208  mJob->cancel();
209  QgsDebugMsg( QStringLiteral( "oveview - deleting old" ) );
210  delete mJob; // get rid of previous job (if any)
211  }
212 
213  QgsDebugMsg( QStringLiteral( "oveview - starting new" ) );
214 
215  // TODO: setup overview mode
218  mJob->start();
219 
221 
222  // schedule repaint
223  update();
224 
225  // update panning widget
226  drawExtentRect();
227 }
228 
230 {
231  QgsDebugMsg( QStringLiteral( "overview - finished" ) );
232  mPixmap = QPixmap::fromImage( mJob->renderedImage() );
233 
234  delete mJob;
235  mJob = nullptr;
236 
237  // schedule repaint
238  update();
239 }
240 
242 {
243  if ( !deferred )
244  refresh();
245 }
246 
247 
248 void QgsMapOverviewCanvas::setBackgroundColor( const QColor &color )
249 {
250  mSettings.setBackgroundColor( color );
251 
252  // set erase color
253  QPalette palette;
254  palette.setColor( backgroundRole(), color );
255  setPalette( palette );
256 }
257 
258 void QgsMapOverviewCanvas::setLayers( const QList<QgsMapLayer *> &layers )
259 {
260  const auto oldLayers = mSettings.layers();
261  for ( QgsMapLayer *ml : oldLayers )
262  {
264  }
265 
267 
268  const auto newLayers = mSettings.layers();
269  for ( QgsMapLayer *ml : newLayers )
270  {
272  }
273 
275 
276  refresh();
277 }
278 
280 {
281  QgsRectangle rect;
282  if ( !QgsProject::instance()->viewSettings()->presetFullExtent().isNull() )
283  {
287  try
288  {
289  rect = ct.transformBoundingBox( extent );
290  }
291  catch ( QgsCsException & )
292  {
293  }
294  }
295 
296  if ( rect.isNull() )
297  {
298  if ( mSettings.hasValidSettings() )
299  rect = mSettings.fullExtent();
300  else
301  rect = mMapCanvas->projectExtent();
302  }
303 
304  // expand a bit to keep features on margin
305  rect.scale( 1.1 );
306 
307  mSettings.setExtent( rect );
308  drawExtentRect();
309 }
310 
312 {
314 }
315 
317 {
319 }
320 
321 QList<QgsMapLayer *> QgsMapOverviewCanvas::layers() const
322 {
323  return mSettings.layers();
324 }
325 
326 
328 
329 QgsPanningWidget::QgsPanningWidget( QWidget *parent )
330  : QWidget( parent )
331 {
332  setObjectName( QStringLiteral( "panningWidget" ) );
333  setMinimumSize( 5, 5 );
334  setAttribute( Qt::WA_NoSystemBackground );
335 }
336 
337 void QgsPanningWidget::setPolygon( const QPolygon &p )
338 {
339  if ( p == mPoly ) return;
340  mPoly = p;
341 
342  //ensure polygon is closed
343  if ( mPoly.at( 0 ) != mPoly.at( mPoly.length() - 1 ) )
344  mPoly.append( mPoly.at( 0 ) );
345 
346  const QRect rect = p.boundingRect() + QMargins( 1, 1, 1, 1 );
347  setGeometry( rect );
348  update();
349 }
350 
351 void QgsPanningWidget::paintEvent( QPaintEvent *pe )
352 {
353  Q_UNUSED( pe )
354 
355  QPainter p;
356 
357  p.begin( this );
358  const QPolygonF t = mPoly.translated( -mPoly.boundingRect().left() + 1, -mPoly.boundingRect().top() + 1 );
359 
360  // drawPolygon causes issues on windows - corners of path may be missing resulting in triangles being drawn
361  // instead of rectangles! (Same cause as #13343)
362  QPainterPath path;
363  path.addPolygon( t );
364 
365  QPen pen;
366  pen.setJoinStyle( Qt::MiterJoin );
367  pen.setColor( Qt::white );
368  pen.setWidth( 3 );
369  p.setPen( pen );
370  p.drawPath( path );
371  pen.setColor( Qt::red );
372  pen.setWidth( 1 );
373  p.setPen( pen );
374  p.drawPath( path );
375 
376  p.end();
377 }
378 
379 
380 
@ 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 SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
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.
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:73
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.
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.
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...
QList< QgsMapLayer * > layers() const
Returns the list of layers which will be rendered in the map.
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:90
A class to represent a 2D point.
Definition: qgspointxy.h:59
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:467
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
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:256
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38