QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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"
24#include "qgsmaptopixel.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
55void 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
69{
70 refresh();
71 QWidget::showEvent( e );
72}
73
74void 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 );
144 }
145}
146
147
149{
150 QgsSettings settings;
151 bool reverseZoom = settings.value( QStringLiteral( "qgis/reverse_wheel_zoom" ), false ).toBool();
152 bool zoomIn = reverseZoom ? e->angleDelta().y() < 0 : e->angleDelta().y() > 0;
153 double zoomFactor = zoomIn ? 1. / mMapCanvas->zoomInFactor() : mMapCanvas->zoomOutFactor();
154
155 // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
156 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
157
158 if ( e->modifiers() & Qt::ControlModifier )
159 {
160 //holding ctrl while wheel zooming results in a finer zoom
161 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
162 }
163
164 const double signedWheelFactor = zoomIn ? 1 / zoomFactor : zoomFactor;
165
166 const QgsMapToPixel &cXf = mSettings.mapToPixel();
167 const QgsPointXY center = cXf.toMapCoordinates( e->position().x(), e->position().y() );
168 updatePanningWidget( QPoint( e->position().x(), e->position().y() ) );
169 mMapCanvas->zoomByFactor( signedWheelFactor, &center );
170}
171
173{
174 // move with panning widget if tracking cursor
175 if ( ( e->buttons() & Qt::LeftButton ) == Qt::LeftButton )
176 {
177 updatePanningWidget( e->pos() );
178 }
179}
180
181
183{
184// if (mPanningWidget->isHidden())
185// return;
186 mPanningWidget->move( pos.x() - mPanningCursorOffset.x(), pos.y() - mPanningCursorOffset.y() );
187}
188
190{
191 if ( !isVisible() )
192 return;
193
195
197 {
198 mPixmap = QPixmap();
199 update();
200 return; // makes no sense to render anything
201 }
202
203 if ( mJob )
204 {
205 QgsDebugMsgLevel( QStringLiteral( "oveview - canceling old" ), 2 );
206 mJob->cancel();
207 QgsDebugMsgLevel( QStringLiteral( "oveview - deleting old" ), 2 );
208 delete mJob; // get rid of previous job (if any)
209 }
210
211 QgsDebugMsgLevel( QStringLiteral( "oveview - starting new" ), 2 );
212
213 // TODO: setup overview mode
216 mJob->start();
217
219
220 // schedule repaint
221 update();
222
223 // update panning widget
225}
226
228{
229 QgsDebugMsgLevel( QStringLiteral( "overview - finished" ), 2 );
230 mPixmap = QPixmap::fromImage( mJob->renderedImage() );
231
232 delete mJob;
233 mJob = nullptr;
234
235 // schedule repaint
236 update();
237}
238
240{
241 if ( !deferred )
242 refresh();
243}
244
245
247{
249
250 // set erase color
251 QPalette palette;
252 palette.setColor( backgroundRole(), color );
253 setPalette( palette );
254}
255
256void QgsMapOverviewCanvas::setLayers( const QList<QgsMapLayer *> &layers )
257{
258 const auto oldLayers = mSettings.layers();
259 for ( QgsMapLayer *ml : oldLayers )
260 {
262 }
263
265
266 const auto newLayers = mSettings.layers();
267 for ( QgsMapLayer *ml : newLayers )
268 {
270 }
271
272 refresh();
273}
274
276{
277 QgsRectangle rect;
278 if ( !QgsProject::instance()->viewSettings()->presetFullExtent().isNull() )
279 {
283 try
284 {
285 rect = ct.transformBoundingBox( extent );
286 }
287 catch ( QgsCsException & )
288 {
289 }
290 }
291
292 if ( rect.isNull() )
293 {
295 rect = mSettings.fullExtent();
296 else
297 rect = mMapCanvas->projectExtent();
298 }
299
300 // expand a bit to keep features on margin
301 rect.scale( 1.1 );
302
303 mSettings.setExtent( rect );
305}
306
308{
310}
311
313{
315}
316
317QList<QgsMapLayer *> QgsMapOverviewCanvas::layers() const
318{
319 return mSettings.layers();
320}
321
322
324
325QgsPanningWidget::QgsPanningWidget( QWidget *parent )
326 : QWidget( parent )
327{
328 setObjectName( QStringLiteral( "panningWidget" ) );
329 setMinimumSize( 5, 5 );
330 setAttribute( Qt::WA_NoSystemBackground );
331}
332
333void QgsPanningWidget::setPolygon( const QPolygon &p )
334{
335 if ( p == mPoly ) return;
336 mPoly = p;
337
338 //ensure polygon is closed
339 if ( mPoly.at( 0 ) != mPoly.at( mPoly.length() - 1 ) )
340 mPoly.append( mPoly.at( 0 ) );
341
342 const QRect rect = p.boundingRect() + QMargins( 1, 1, 1, 1 );
343 setGeometry( rect );
344 update();
345}
346
347void QgsPanningWidget::paintEvent( QPaintEvent *pe )
348{
349 Q_UNUSED( pe )
350
351 QPainter p;
352
353 p.begin( this );
354 const QPolygonF t = mPoly.translated( -mPoly.boundingRect().left() + 1, -mPoly.boundingRect().top() + 1 );
355
356 // drawPolygon causes issues on windows - corners of path may be missing resulting in triangles being drawn
357 // instead of rectangles! (Same cause as #13343)
358 QPainterPath path;
359 path.addPolygon( t );
360
361 QPen pen;
362 pen.setJoinStyle( Qt::MiterJoin );
363 pen.setColor( Qt::white );
364 pen.setWidth( 3 );
365 p.setPen( pen );
366 p.drawPath( path );
367 pen.setColor( Qt::red );
368 pen.setWidth( 1 );
369 p.setPen( pen );
370 p.drawPath( path );
371
372 p.end();
373}
374
375
376
@ 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:67
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
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.
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.
QColor backgroundColor() const
Returns the background color of the map.
const QgsMapToPixel & mapToPixel() const
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
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: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:484
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: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.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39