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