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