QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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"
23#include "moc_qgsmapoverviewcanvas.cpp"
25#include "qgsmaptopixel.h"
27#include "qgslogger.h"
28
29#include <QPainter>
30#include <QPainterPath>
31#include <QPaintEvent>
32#include <QResizeEvent>
33#include <QMouseEvent>
34#include <limits>
35
36
38 : QWidget( parent )
39 , mMapCanvas( mapCanvas )
40
41{
42 setAutoFillBackground( true );
43 setObjectName( QStringLiteral( "theOverviewCanvas" ) );
44 mPanningWidget = new QgsPanningWidget( this );
45
48
53
55}
56
57void QgsMapOverviewCanvas::resizeEvent( QResizeEvent *e )
58{
59 mPixmap = QPixmap();
60
61 mSettings.setOutputSize( e->size() );
62
64
65 refresh();
66
67 QWidget::resizeEvent( e );
68}
69
71{
72 refresh();
73 QWidget::showEvent( e );
74}
75
76void QgsMapOverviewCanvas::paintEvent( QPaintEvent *pe )
77{
78 QPainter paint( this );
79 QRect rect = pe->rect();
80 QRect sourceRect( std::ceil( pe->rect().left() * mPixmap.devicePixelRatio() ), std::ceil( pe->rect().top() * mPixmap.devicePixelRatio() ), std::ceil( pe->rect().width() * mPixmap.devicePixelRatio() ), std::ceil( pe->rect().height() * mPixmap.devicePixelRatio() ) );
81 if ( !mPixmap.isNull() )
82 {
83 paint.drawPixmap( rect.topLeft(), mPixmap, sourceRect );
84 }
85 else
86 {
87 paint.fillRect( rect, QBrush( mSettings.backgroundColor() ) );
88 }
89}
90
91
93{
94 if ( !mMapCanvas )
95 return;
96
97 const QgsRectangle &extent = mMapCanvas->extent();
98
99 // show only when valid extent is set
100 if ( extent.isEmpty() || mSettings.visibleExtent().isEmpty() )
101 {
102 mPanningWidget->hide();
103 return;
104 }
105
106 const QPolygonF &vPoly = mMapCanvas->mapSettings().visiblePolygon();
107 const QgsMapToPixel &cXf = mSettings.mapToPixel();
108 QVector<QPoint> pts;
109 pts.push_back( cXf.transform( QgsPointXY( vPoly[0] ) ).toQPointF().toPoint() );
110 pts.push_back( cXf.transform( QgsPointXY( vPoly[1] ) ).toQPointF().toPoint() );
111 pts.push_back( cXf.transform( QgsPointXY( vPoly[2] ) ).toQPointF().toPoint() );
112 pts.push_back( cXf.transform( QgsPointXY( vPoly[3] ) ).toQPointF().toPoint() );
113 mPanningWidget->setPolygon( QPolygon( pts ) );
114 mPanningWidget->show(); // show if hidden
115}
116
117
119{
120 // if (mPanningWidget->isHidden())
121 // return;
122
123 // set offset in panning widget if inside it
124 // for better experience with panning :)
125 if ( mPanningWidget->geometry().contains( e->pos() ) )
126 {
127 mPanningCursorOffset = e->pos() - mPanningWidget->pos();
128 }
129 else
130 {
131 // use center of the panning widget if outside
132 const QSize s = mPanningWidget->size();
133 mPanningCursorOffset = QPoint( s.width() / 2, s.height() / 2 );
134 }
135 updatePanningWidget( e->pos() );
136}
137
138
140{
141 // if (mPanningWidget->isHidden())
142 // return;
143
144 if ( e->button() == Qt::LeftButton )
145 {
146 // set new extent
147 const QgsMapToPixel &cXf = mSettings.mapToPixel();
148 const QRect rect = mPanningWidget->geometry();
149
150 const QgsPointXY center = cXf.toMapCoordinates( rect.center() );
151 mMapCanvas->setCenter( center );
153 }
154}
155
156
158{
159 QgsSettings settings;
160 bool reverseZoom = settings.value( QStringLiteral( "qgis/reverse_wheel_zoom" ), false ).toBool();
161 bool zoomIn = reverseZoom ? e->angleDelta().y() < 0 : e->angleDelta().y() > 0;
162 double zoomFactor = zoomIn ? 1. / mMapCanvas->zoomInFactor() : mMapCanvas->zoomOutFactor();
163
164 // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
165 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
166
167 if ( e->modifiers() & Qt::ControlModifier )
168 {
169 //holding ctrl while wheel zooming results in a finer zoom
170 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
171 }
172
173 const double signedWheelFactor = zoomIn ? 1 / zoomFactor : zoomFactor;
174
175 const QgsMapToPixel &cXf = mSettings.mapToPixel();
176 const QgsPointXY center = cXf.toMapCoordinates( e->position().x(), e->position().y() );
177 updatePanningWidget( QPoint( e->position().x(), e->position().y() ) );
178 mMapCanvas->zoomByFactor( signedWheelFactor, &center );
179}
180
182{
183 // move with panning widget if tracking cursor
184 if ( ( e->buttons() & Qt::LeftButton ) == Qt::LeftButton )
185 {
186 updatePanningWidget( e->pos() );
187 }
188}
189
190
192{
193 // if (mPanningWidget->isHidden())
194 // return;
195 mPanningWidget->move( pos.x() - mPanningCursorOffset.x(), pos.y() - mPanningCursorOffset.y() );
196}
197
199{
200 if ( !isVisible() )
201 return;
202
204
206 {
207 mPixmap = QPixmap();
208 update();
209 return; // makes no sense to render anything
210 }
211
212 if ( mJob )
213 {
214 QgsDebugMsgLevel( QStringLiteral( "oveview - canceling old" ), 2 );
215 mJob->cancel();
216 QgsDebugMsgLevel( QStringLiteral( "oveview - deleting old" ), 2 );
217 delete mJob; // get rid of previous job (if any)
218 }
219
220 QgsDebugMsgLevel( QStringLiteral( "oveview - starting new" ), 2 );
221
222 mSettings.setDevicePixelRatio( static_cast<float>( devicePixelRatioF() ) );
223
224 // TODO: setup overview mode
227 mJob->start();
228
230
231 // schedule repaint
232 update();
233
234 // update panning widget
236}
237
239{
240 QgsDebugMsgLevel( QStringLiteral( "overview - finished" ), 2 );
241 mPixmap = QPixmap::fromImage( mJob->renderedImage() );
242
243 delete mJob;
244 mJob = nullptr;
245
246 // schedule repaint
247 update();
248}
249
251{
252 if ( !deferred )
253 refresh();
254}
255
256
258{
260
261 // set erase color
262 QPalette palette;
263 palette.setColor( backgroundRole(), color );
264 setPalette( palette );
265}
266
267void QgsMapOverviewCanvas::setLayers( const QList<QgsMapLayer *> &layers )
268{
269 const auto oldLayers = mSettings.layers();
270 for ( QgsMapLayer *ml : oldLayers )
271 {
273 }
274
276
277 const auto newLayers = mSettings.layers();
278 for ( QgsMapLayer *ml : newLayers )
279 {
281 }
282
283 refresh();
284}
285
287{
288 QgsRectangle rect;
289 if ( !QgsProject::instance()->viewSettings()->presetFullExtent().isNull() )
290 {
294 try
295 {
296 rect = ct.transformBoundingBox( extent );
297 }
298 catch ( QgsCsException & )
299 {
300 }
301 }
302
303 if ( rect.isNull() )
304 {
306 rect = mSettings.fullExtent();
307 else
308 rect = mMapCanvas->projectExtent();
309 }
310
311 // expand a bit to keep features on margin
312 rect.scale( 1.1 );
313
314 mSettings.setExtent( rect );
316}
317
322
327
328QList<QgsMapLayer *> QgsMapOverviewCanvas::layers() const
329{
330 return mSettings.layers();
331}
332
333
335
336QgsPanningWidget::QgsPanningWidget( QWidget *parent )
337 : QWidget( parent )
338{
339 setObjectName( QStringLiteral( "panningWidget" ) );
340 setMinimumSize( 5, 5 );
341 setAttribute( Qt::WA_NoSystemBackground );
342}
343
344void QgsPanningWidget::setPolygon( const QPolygon &p )
345{
346 if ( p == mPoly )
347 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
359void 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
@ 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.
Map canvas is a class for displaying all GIS data types on a canvas.
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:76
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.
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.
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.
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.
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.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
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