QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
20
21#include <limits>
22
23#include "qgslogger.h"
24#include "qgsmapcanvas.h"
25#include "qgsmaplayer.h"
27#include "qgsmaptopixel.h"
28#include "qgsproject.h"
30
31#include <QMouseEvent>
32#include <QPaintEvent>
33#include <QPainter>
34#include <QPainterPath>
35#include <QResizeEvent>
36
37#include "moc_qgsmapoverviewcanvas.cpp"
38
40 : QWidget( parent )
41 , mMapCanvas( mapCanvas )
42
43{
44 setAutoFillBackground( true );
45 setObjectName( QStringLiteral( "theOverviewCanvas" ) );
46 mPanningWidget = new QgsPanningWidget( this );
47
48 mSettings.setTransformContext( mMapCanvas->mapSettings().transformContext() );
50
55
57}
58
59void QgsMapOverviewCanvas::resizeEvent( QResizeEvent *e )
60{
61 mPixmap = QPixmap();
62
63 mSettings.setOutputSize( e->size() );
64
66
67 refresh();
68
69 QWidget::resizeEvent( e );
70}
71
73{
74 refresh();
75 QWidget::showEvent( e );
76}
77
78void QgsMapOverviewCanvas::paintEvent( QPaintEvent *pe )
79{
80 QPainter paint( this );
81 QRect rect = pe->rect();
82 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() ) );
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 )
97 return;
98
99 const QgsRectangle &extent = mMapCanvas->extent();
100
101 // show only when valid extent is set
102 if ( extent.isEmpty() || mSettings.visibleExtent().isEmpty() )
103 {
104 mPanningWidget->hide();
105 return;
106 }
107
108 const QPolygonF &vPoly = mMapCanvas->mapSettings().visiblePolygon();
109 const QgsMapToPixel &cXf = mSettings.mapToPixel();
110 QVector<QPoint> pts;
111 pts.push_back( cXf.transform( QgsPointXY( vPoly[0] ) ).toQPointF().toPoint() );
112 pts.push_back( cXf.transform( QgsPointXY( vPoly[1] ) ).toQPointF().toPoint() );
113 pts.push_back( cXf.transform( QgsPointXY( vPoly[2] ) ).toQPointF().toPoint() );
114 pts.push_back( cXf.transform( QgsPointXY( vPoly[3] ) ).toQPointF().toPoint() );
115 mPanningWidget->setPolygon( QPolygon( pts ) );
116 mPanningWidget->show(); // show if hidden
117}
118
119
121{
122 // if (mPanningWidget->isHidden())
123 // return;
124
125 // set offset in panning widget if inside it
126 // for better experience with panning :)
127 if ( mPanningWidget->geometry().contains( e->pos() ) )
128 {
129 mPanningCursorOffset = e->pos() - mPanningWidget->pos();
130 }
131 else
132 {
133 // use center of the panning widget if outside
134 const QSize s = mPanningWidget->size();
135 mPanningCursorOffset = QPoint( s.width() / 2, s.height() / 2 );
136 }
137 updatePanningWidget( e->pos() );
138}
139
140
142{
143 // if (mPanningWidget->isHidden())
144 // return;
145
146 if ( e->button() == Qt::LeftButton )
147 {
148 // set new extent
149 const QgsMapToPixel &cXf = mSettings.mapToPixel();
150 const QRect rect = mPanningWidget->geometry();
151
152 const QgsPointXY center = cXf.toMapCoordinates( rect.center() );
153 mMapCanvas->setCenter( center );
154 mMapCanvas->refresh();
155 }
156}
157
158
160{
161 QgsSettings settings;
162 bool reverseZoom = settings.value( QStringLiteral( "qgis/reverse_wheel_zoom" ), false ).toBool();
163 bool zoomIn = reverseZoom ? e->angleDelta().y() < 0 : e->angleDelta().y() > 0;
164 double zoomFactor = zoomIn ? 1. / mMapCanvas->zoomInFactor() : mMapCanvas->zoomOutFactor();
165
166 // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
167 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() );
168
169 if ( e->modifiers() & Qt::ControlModifier )
170 {
171 //holding ctrl while wheel zooming results in a finer zoom
172 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
173 }
174
175 const double signedWheelFactor = zoomIn ? 1 / zoomFactor : zoomFactor;
176
177 const QgsMapToPixel &cXf = mSettings.mapToPixel();
178 const QgsPointXY center = cXf.toMapCoordinates( e->position().x(), e->position().y() );
179 updatePanningWidget( QPoint( e->position().x(), e->position().y() ) );
180 mMapCanvas->zoomByFactor( signedWheelFactor, &center );
181}
182
184{
185 // move with panning widget if tracking cursor
186 if ( ( e->buttons() & Qt::LeftButton ) == Qt::LeftButton )
187 {
188 updatePanningWidget( e->pos() );
189 }
190}
191
192
194{
195 // if (mPanningWidget->isHidden())
196 // return;
197 mPanningWidget->move( pos.x() - mPanningCursorOffset.x(), pos.y() - mPanningCursorOffset.y() );
198}
199
201{
202 if ( !isVisible() )
203 return;
204
206
207 if ( !mSettings.hasValidSettings() )
208 {
209 mPixmap = QPixmap();
210 update();
211 return; // makes no sense to render anything
212 }
213
214 if ( mJob )
215 {
216 QgsDebugMsgLevel( QStringLiteral( "oveview - canceling old" ), 2 );
217 mJob->cancel();
218 QgsDebugMsgLevel( QStringLiteral( "oveview - deleting old" ), 2 );
219 delete mJob; // get rid of previous job (if any)
220 }
221
222 QgsDebugMsgLevel( QStringLiteral( "oveview - starting new" ), 2 );
223
224 mSettings.setDevicePixelRatio( static_cast<float>( devicePixelRatioF() ) );
225
226 // TODO: setup overview mode
229 mJob->start();
230
231 setBackgroundColor( mMapCanvas->mapSettings().backgroundColor() );
232
233 // schedule repaint
234 update();
235
236 // update panning widget
238}
239
241{
242 QgsDebugMsgLevel( QStringLiteral( "overview - finished" ), 2 );
243 mPixmap = QPixmap::fromImage( mJob->renderedImage() );
244
245 delete mJob;
246 mJob = nullptr;
247
248 // schedule repaint
249 update();
250}
251
253{
254 if ( !deferred )
255 refresh();
256}
257
258
260{
261 mSettings.setBackgroundColor( color );
262
263 // set erase color
264 QPalette palette;
265 palette.setColor( backgroundRole(), color );
266 setPalette( palette );
267}
268
269void QgsMapOverviewCanvas::setLayers( const QList<QgsMapLayer *> &layers )
270{
271 const auto oldLayers = mSettings.layers();
272 for ( QgsMapLayer *ml : oldLayers )
273 {
275 }
276
277 mSettings.setLayers( layers );
278
279 const auto newLayers = mSettings.layers();
280 for ( QgsMapLayer *ml : newLayers )
281 {
283 }
284
285 refresh();
286}
287
289{
290 QgsRectangle rect;
291 if ( !QgsProject::instance()->viewSettings()->presetFullExtent().isNull() )
292 {
294 QgsCoordinateTransform ct( extent.crs(), mSettings.destinationCrs(), QgsProject::instance()->transformContext() );
296 try
297 {
298 rect = ct.transformBoundingBox( extent );
299 }
300 catch ( QgsCsException & )
301 {
302 }
303 }
304
305 if ( rect.isNull() )
306 {
307 if ( mSettings.hasValidSettings() )
308 rect = mSettings.fullExtent();
309 else
310 rect = mMapCanvas->projectExtent();
311 }
312
313 // expand a bit to keep features on margin
314 rect.scale( 1.1 );
315
316 mSettings.setExtent( rect );
318}
319
321{
322 mSettings.setDestinationCrs( mMapCanvas->mapSettings().destinationCrs() );
323}
324
326{
327 mSettings.setTransformContext( mMapCanvas->mapSettings().transformContext() );
328}
329
330QList<QgsMapLayer *> QgsMapOverviewCanvas::layers() const
331{
332 return mSettings.layers();
333}
334
335
337
338QgsPanningWidget::QgsPanningWidget( QWidget *parent )
339 : QWidget( parent )
340{
341 setObjectName( QStringLiteral( "panningWidget" ) );
342 setMinimumSize( 5, 5 );
343 setAttribute( Qt::WA_NoSystemBackground );
344}
345
346void QgsPanningWidget::setPolygon( const QPolygon &p )
347{
348 if ( p == mPoly )
349 return;
350 mPoly = p;
351
352 //ensure polygon is closed
353 if ( mPoly.at( 0 ) != mPoly.at( mPoly.length() - 1 ) )
354 mPoly.append( mPoly.at( 0 ) );
355
356 const QRect rect = p.boundingRect() + QMargins( 1, 1, 1, 1 );
357 setGeometry( rect );
358 update();
359}
360
361void QgsPanningWidget::paintEvent( QPaintEvent *pe )
362{
363 Q_UNUSED( pe )
364
365 QPainter p;
366
367 p.begin( this );
368 const QPolygonF t = mPoly.translated( -mPoly.boundingRect().left() + 1, -mPoly.boundingRect().top() + 1 );
369
370 // drawPolygon causes issues on windows - corners of path may be missing resulting in triangles being drawn
371 // instead of rectangles! (Same cause as #13343)
372 QPainterPath path;
373 path.addPolygon( t );
374
375 QPen pen;
376 pen.setJoinStyle( Qt::MiterJoin );
377 pen.setColor( Qt::white );
378 pen.setWidth( 3 );
379 p.setPen( pen );
380 p.drawPath( path );
381 pen.setColor( Qt::red );
382 pen.setWidth( 1 );
383 p.setPen( pen );
384 p.drawPath( path );
385
386 p.end();
387}
388
389
@ DrawLabeling
Enable drawing of labels on top of the map.
Definition qgis.h:2721
Handles coordinate transforms between two 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 canvasColorChanged()
Emitted when canvas background color changes.
void destinationCrsChanged()
Emitted when map CRS has changed.
void transformContextChanged()
Emitted when the canvas transform context is changed.
Base class for all map layer types.
Definition qgsmaplayer.h:80
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).
Job implementation that renders everything sequentially in one thread.
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.
Represents 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.
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.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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:61