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