Skip to content

Commit 6acd326

Browse files
authoredJul 18, 2017
[FEATURE] Draw extent onto canvas in save as image/PDF dialog (#4878)
1 parent 71b9ce2 commit 6acd326

10 files changed

+411
-2
lines changed
 

‎python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
%Include qgsmaptoolcapture.sip
138138
%Include qgsmaptooledit.sip
139139
%Include qgsmaptoolemitpoint.sip
140+
%Include qgsmaptoolextent.sip
140141
%Include qgsmaptoolidentify.sip
141142
%Include qgsmaptoolidentifyfeature.sip
142143
%Include qgsmaptoolpan.sip

‎python/gui/qgsextentgroupbox.sip

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111

1212

13+
14+
1315
class QgsExtentGroupBox : QgsCollapsibleGroupBox
1416
{
1517
%Docstring
@@ -34,6 +36,7 @@ class QgsExtentGroupBox : QgsCollapsibleGroupBox
3436
CurrentExtent,
3537
UserExtent,
3638
ProjectLayerExtent,
39+
DrawOnCanvas,
3740
};
3841

3942
explicit QgsExtentGroupBox( QWidget *parent /TransferThis/ = 0 );
@@ -124,6 +127,13 @@ class QgsExtentGroupBox : QgsCollapsibleGroupBox
124127
:rtype: str
125128
%End
126129

130+
void setMapCanvas( QgsMapCanvas *canvas );
131+
%Docstring
132+
Sets the map canvas to enable dragging of extent on a canvas.
133+
\param canvas the map canvas
134+
.. versionadded:: 3.0
135+
%End
136+
127137
public slots:
128138

129139
void setOutputExtentFromOriginal();
@@ -145,6 +155,12 @@ class QgsExtentGroupBox : QgsCollapsibleGroupBox
145155
%Docstring
146156
Sets the output extent to match a ``layer``'s extent (may be transformed to output CRS).
147157
.. versionadded:: 3.0
158+
%End
159+
160+
void setOutputExtentFromDrawOnCanvas();
161+
%Docstring
162+
Sets the output extent by dragging on the canvas.
163+
.. versionadded:: 3.0
148164
%End
149165

150166
signals:

‎python/gui/qgsmaptoolextent.sip

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgsmaptoolextent.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
14+
class QgsMapToolExtent : QgsMapTool
15+
{
16+
%Docstring
17+
A map tool that emits an extent from a rectangle drawn onto the map canvas.
18+
.. versionadded:: 3.0
19+
%End
20+
21+
%TypeHeaderCode
22+
#include "qgsmaptoolextent.h"
23+
%End
24+
public:
25+
26+
QgsMapToolExtent( QgsMapCanvas *canvas );
27+
%Docstring
28+
constructor
29+
%End
30+
31+
virtual Flags flags() const;
32+
virtual void canvasMoveEvent( QgsMapMouseEvent *e );
33+
virtual void canvasPressEvent( QgsMapMouseEvent *e );
34+
virtual void canvasReleaseEvent( QgsMapMouseEvent *e );
35+
virtual void activate();
36+
virtual void deactivate();
37+
38+
void setRatio( QSize ratio );
39+
%Docstring
40+
Sets a fixed aspect ratio to be used when dragging extent onto the canvas.
41+
To unset a fixed aspect ratio, set the width and height to zero.
42+
\param ratio aspect ratio's width and height
43+
*
44+
%End
45+
46+
QSize ratio() const;
47+
%Docstring
48+
Returns the current fixed aspect ratio to be used when dragging extent onto the canvas.
49+
If the aspect ratio isn't fixed, the width and height will be set to zero.
50+
*
51+
:rtype: QSize
52+
%End
53+
54+
QgsRectangle extent() const;
55+
%Docstring
56+
Returns the current extent drawn onto the canvas.
57+
:rtype: QgsRectangle
58+
%End
59+
60+
signals:
61+
62+
void extentChanged( const QgsRectangle &extent );
63+
%Docstring
64+
signal emitted on extent change
65+
%End
66+
67+
};
68+
69+
/************************************************************************
70+
* This file has been generated automatically from *
71+
* *
72+
* src/gui/qgsmaptoolextent.h *
73+
* *
74+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
75+
************************************************************************/

‎src/app/qgsmapsavedialog.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, QL
5959
mExtentGroupBox->setOutputCrs( ms.destinationCrs() );
6060
mExtentGroupBox->setCurrentExtent( mExtent, ms.destinationCrs() );
6161
mExtentGroupBox->setOutputExtentFromCurrent();
62+
mExtentGroupBox->setMapCanvas( mapCanvas );
6263

6364
mScaleWidget->setScale( ms.scale() );
6465
mScaleWidget->setMapCanvas( mMapCanvas );

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ SET(QGIS_GUI_SRCS
283283
qgsmaptoolcapture.cpp
284284
qgsmaptooledit.cpp
285285
qgsmaptoolemitpoint.cpp
286+
qgsmaptoolextent.cpp
286287
qgsmaptoolidentify.cpp
287288
qgsmaptoolidentifyfeature.cpp
288289
qgsmaptoolpan.cpp
@@ -437,6 +438,7 @@ SET(QGIS_GUI_MOC_HDRS
437438
qgsmaptoolcapture.h
438439
qgsmaptooledit.h
439440
qgsmaptoolemitpoint.h
441+
qgsmaptoolextent.h
440442
qgsmaptoolidentify.h
441443
qgsmaptoolidentifyfeature.h
442444
qgsmaptoolpan.h

‎src/gui/qgsextentgroupbox.cpp

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212
* (at your option) any later version. *
1313
* *
1414
***************************************************************************/
15+
1516
#include "qgsextentgroupbox.h"
1617

18+
#include "qgslogger.h"
1719
#include "qgscoordinatetransform.h"
1820
#include "qgsrasterblock.h"
21+
#include "qgsmapcanvas.h"
1922
#include "qgsmaplayermodel.h"
2023
#include "qgsexception.h"
2124
#include "qgsproject.h"
25+
2226
#include <QMenu>
2327
#include <QAction>
2428

@@ -40,13 +44,15 @@ QgsExtentGroupBox::QgsExtentGroupBox( QWidget *parent )
4044
mYMaxLineEdit->setValidator( new QDoubleValidator( this ) );
4145

4246
mOriginalExtentButton->setVisible( false );
47+
mButtonDrawOnCanvas->setVisible( false );
4348

4449
connect( mCurrentExtentButton, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromCurrent );
4550
connect( mOriginalExtentButton, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromOriginal );
51+
connect( mButtonDrawOnCanvas, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromDrawOnCanvas );
52+
4653
connect( this, &QGroupBox::clicked, this, &QgsExtentGroupBox::groupBoxClicked );
4754
}
4855

49-
5056
void QgsExtentGroupBox::setOriginalExtent( const QgsRectangle &originalExtent, const QgsCoordinateReferenceSystem &originalCrs )
5157
{
5258
mOriginalExtent = originalExtent;
@@ -83,6 +89,11 @@ void QgsExtentGroupBox::setOutputCrs( const QgsCoordinateReferenceSystem &output
8389
setOutputExtentFromLayer( mExtentLayer.data() );
8490
break;
8591

92+
case DrawOnCanvas:
93+
mOutputCrs = outputCrs;
94+
extentDrawn( outputExtent() );
95+
break;
96+
8697
case UserExtent:
8798
try
8899
{
@@ -167,6 +178,9 @@ void QgsExtentGroupBox::updateTitle()
167178
case ProjectLayerExtent:
168179
msg = mExtentLayerName;
169180
break;
181+
case DrawOnCanvas:
182+
msg = tr( "drawn on canvas" );
183+
break;
170184
}
171185
if ( isCheckable() && !isChecked() )
172186
msg = tr( "none" );
@@ -213,7 +227,17 @@ void QgsExtentGroupBox::setExtentToLayerExtent( const QString &layerId )
213227

214228
void QgsExtentGroupBox::setOutputExtentFromCurrent()
215229
{
216-
setOutputExtent( mCurrentExtent, mCurrentCrs, CurrentExtent );
230+
if ( mCanvas )
231+
{
232+
// Use unrotated visible extent to insure output size and scale matches canvas
233+
QgsMapSettings ms = mCanvas->mapSettings();
234+
ms.setRotation( 0 );
235+
setOutputExtent( ms.visibleExtent(), ms.destinationCrs(), CurrentExtent );
236+
}
237+
else
238+
{
239+
setOutputExtent( mCurrentExtent, mCurrentCrs, CurrentExtent );
240+
}
217241
}
218242

219243

@@ -238,6 +262,34 @@ void QgsExtentGroupBox::setOutputExtentFromLayer( const QgsMapLayer *layer )
238262
setOutputExtent( layer->extent(), layer->crs(), ProjectLayerExtent );
239263
}
240264

265+
void QgsExtentGroupBox::setOutputExtentFromDrawOnCanvas()
266+
{
267+
if ( mCanvas )
268+
{
269+
mMapToolPrevious = mCanvas->mapTool();
270+
if ( !mMapToolExtent )
271+
{
272+
mMapToolExtent.reset( new QgsMapToolExtent( mCanvas ) );
273+
connect( mMapToolExtent.get(), &QgsMapToolExtent::extentChanged, this, &QgsExtentGroupBox::extentDrawn );
274+
connect( mMapToolExtent.get(), &QgsMapTool::deactivated, this, [ = ]
275+
{
276+
window()->setVisible( true );
277+
mMapToolPrevious = nullptr;
278+
} );
279+
}
280+
mCanvas->setMapTool( mMapToolExtent.get() );
281+
window()->setVisible( false );
282+
}
283+
}
284+
285+
void QgsExtentGroupBox::extentDrawn( const QgsRectangle &extent )
286+
{
287+
setOutputExtent( extent, mCanvas->mapSettings().destinationCrs(), DrawOnCanvas );
288+
mCanvas->setMapTool( mMapToolPrevious );
289+
window()->setVisible( true );
290+
mMapToolPrevious = nullptr;
291+
}
292+
241293
void QgsExtentGroupBox::groupBoxClicked()
242294
{
243295
if ( !isCheckable() )
@@ -269,3 +321,16 @@ QString QgsExtentGroupBox::titleBase() const
269321
{
270322
return mTitleBase;
271323
}
324+
325+
void QgsExtentGroupBox::setMapCanvas( QgsMapCanvas *canvas )
326+
{
327+
if ( canvas )
328+
{
329+
mCanvas = canvas;
330+
mButtonDrawOnCanvas->setVisible( true );
331+
}
332+
else
333+
{
334+
mButtonDrawOnCanvas->setVisible( false );
335+
}
336+
}

‎src/gui/qgsextentgroupbox.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212
* (at your option) any later version. *
1313
* *
1414
***************************************************************************/
15+
1516
#ifndef QGSEXTENTGROUPBOX_H
1617
#define QGSEXTENTGROUPBOX_H
1718

1819
#include "qgscollapsiblegroupbox.h"
20+
#include "qgsmaptool.h"
21+
#include "qgsmaptoolextent.h"
1922
#include "qgis.h"
2023

2124
#include "ui_qgsextentgroupboxwidget.h"
@@ -24,6 +27,8 @@
2427
#include "qgsrectangle.h"
2528
#include "qgis_gui.h"
2629

30+
#include <memory>
31+
2732
class QgsCoordinateReferenceSystem;
2833
class QgsMapLayerModel;
2934
class QgsMapLayer;
@@ -52,6 +57,7 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
5257
CurrentExtent, //!< Map canvas extent
5358
UserExtent, //!< Extent manually entered/modified by the user
5459
ProjectLayerExtent, //!< Extent taken from a layer within the project
60+
DrawOnCanvas, //!< Extent taken from a rectangled drawn onto the map canvas
5561
};
5662

5763
/**
@@ -135,6 +141,13 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
135141
*/
136142
QString titleBase() const;
137143

144+
/**
145+
* Sets the map canvas to enable dragging of extent on a canvas.
146+
* \param canvas the map canvas
147+
* \since QGIS 3.0
148+
*/
149+
void setMapCanvas( QgsMapCanvas *canvas );
150+
138151
public slots:
139152

140153
/**
@@ -158,6 +171,12 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
158171
*/
159172
void setOutputExtentFromLayer( const QgsMapLayer *layer );
160173

174+
/**
175+
* Sets the output extent by dragging on the canvas.
176+
* \since QGIS 3.0
177+
*/
178+
void setOutputExtentFromDrawOnCanvas();
179+
161180
signals:
162181

163182
/**
@@ -175,6 +194,8 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
175194
void groupBoxClicked();
176195
void layerMenuAboutToShow();
177196

197+
void extentDrawn( const QgsRectangle &extent );
198+
178199
private:
179200
void setOutputExtent( const QgsRectangle &r, const QgsCoordinateReferenceSystem &srcCrs, QgsExtentGroupBox::ExtentState state );
180201
void setOutputExtentFromLineEdit();
@@ -199,6 +220,10 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
199220
QPointer< const QgsMapLayer > mExtentLayer;
200221
QString mExtentLayerName;
201222

223+
std::unique_ptr< QgsMapToolExtent > mMapToolExtent;
224+
QPointer< QgsMapTool > mMapToolPrevious = nullptr;
225+
QgsMapCanvas *mCanvas = nullptr;
226+
202227
void setExtentToLayerExtent( const QString &layerId );
203228

204229
};

‎src/gui/qgsmaptoolextent.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/***************************************************************************
2+
qgsmaptoolextent.h - map tool that emits an extent
3+
---------------------
4+
begin : July 2017
5+
copyright : (C) 2017 by Mathieu Pellerin
6+
email : nirvn dot asia at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmaptoolextent.h"
17+
#include "qgsmapcanvas.h"
18+
#include "qgswkbtypes.h"
19+
20+
#include <QMouseEvent>
21+
22+
23+
QgsMapToolExtent::QgsMapToolExtent( QgsMapCanvas *canvas )
24+
: QgsMapTool( canvas )
25+
{
26+
mRubberBand.reset( new QgsRubberBand( canvas, QgsWkbTypes::PolygonGeometry ) );
27+
}
28+
29+
void QgsMapToolExtent::activate()
30+
{
31+
QgsMapTool::activate();
32+
}
33+
34+
void QgsMapToolExtent::deactivate()
35+
{
36+
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
37+
38+
QgsMapTool::deactivate();
39+
}
40+
41+
void QgsMapToolExtent::canvasMoveEvent( QgsMapMouseEvent *e )
42+
{
43+
if ( !mDraw )
44+
return;
45+
46+
47+
QgsPointXY p = toMapCoordinates( e->pos() );
48+
if ( mRatio.width() > 0 && mRatio.height() > 0 )
49+
{
50+
double width = qAbs( p.x() - mStartPoint.x() );
51+
double height = width * ( mRatio.width() / mRatio.height() );
52+
if ( p.y() - mStartPoint.y() < 0 )
53+
height *= -1;
54+
p.setY( mStartPoint.y() + height );
55+
}
56+
57+
mEndPoint = toMapCoordinates( e->pos() );
58+
calculateEndPoint( mEndPoint );
59+
drawExtent();
60+
}
61+
62+
void QgsMapToolExtent::canvasPressEvent( QgsMapMouseEvent *e )
63+
{
64+
mStartPoint = toMapCoordinates( e->pos() );
65+
mEndPoint = mStartPoint;
66+
drawExtent();
67+
68+
mDraw = true;
69+
}
70+
71+
void QgsMapToolExtent::canvasReleaseEvent( QgsMapMouseEvent *e )
72+
{
73+
if ( !mDraw )
74+
return;
75+
76+
mEndPoint = toMapCoordinates( e->pos() );
77+
calculateEndPoint( mEndPoint );
78+
drawExtent();
79+
80+
emit extentChanged( extent() );
81+
82+
mDraw = false;
83+
}
84+
85+
QgsRectangle QgsMapToolExtent::extent() const
86+
{
87+
if ( mStartPoint.x() != mEndPoint.x() && mStartPoint.y() != mEndPoint.y() )
88+
{
89+
return QgsRectangle( mStartPoint, mEndPoint );
90+
}
91+
else
92+
{
93+
return QgsRectangle();
94+
}
95+
}
96+
97+
void QgsMapToolExtent::calculateEndPoint( QgsPointXY &point )
98+
{
99+
if ( mRatio.width() > 0 && mRatio.height() > 0 )
100+
{
101+
double width = qAbs( point.x() - mStartPoint.x() );
102+
double height = width * mRatio.height() / mRatio.width();
103+
if ( point.y() - mStartPoint.y() < 0 )
104+
height *= -1;
105+
point.setY( mStartPoint.y() + height );
106+
}
107+
}
108+
109+
void QgsMapToolExtent::drawExtent()
110+
{
111+
if ( qgsDoubleNear( mStartPoint.x(), mEndPoint.x() ) && qgsDoubleNear( mStartPoint.y(), mEndPoint.y() ) )
112+
return;
113+
114+
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
115+
116+
QgsRectangle rect( mStartPoint, mEndPoint );
117+
118+
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
119+
mRubberBand->addPoint( QgsPointXY( rect.xMinimum(), rect.yMinimum() ), false );
120+
mRubberBand->addPoint( QgsPointXY( rect.xMaximum(), rect.yMinimum() ), false );
121+
mRubberBand->addPoint( QgsPointXY( rect.xMaximum(), rect.yMaximum() ), false );
122+
mRubberBand->addPoint( QgsPointXY( rect.xMinimum(), rect.yMaximum() ), true );
123+
124+
mRubberBand->show();
125+
}

‎src/gui/qgsmaptoolextent.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/***************************************************************************
2+
qgsmaptoolextent.h - map tool that emits an extent
3+
---------------------
4+
begin : July 2017
5+
copyright : (C) 2017 by Mathieu Pellerin
6+
email : nirvn dot asia at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMAPTOOLEXTENT_H
17+
#define QGSMAPTOOLEXTENT_H
18+
19+
#include "qgsmaptool.h"
20+
#include "qgspointxy.h"
21+
#include "qgsrubberband.h"
22+
#include "qgis_gui.h"
23+
24+
#include <memory>
25+
26+
class QgsMapCanvas;
27+
28+
29+
/** \ingroup gui
30+
* A map tool that emits an extent from a rectangle drawn onto the map canvas.
31+
* \since QGIS 3.0
32+
*/
33+
class GUI_EXPORT QgsMapToolExtent : public QgsMapTool
34+
{
35+
Q_OBJECT
36+
37+
public:
38+
39+
//! constructor
40+
QgsMapToolExtent( QgsMapCanvas *canvas );
41+
42+
virtual Flags flags() const override { return QgsMapTool::AllowZoomRect; }
43+
virtual void canvasMoveEvent( QgsMapMouseEvent *e ) override;
44+
virtual void canvasPressEvent( QgsMapMouseEvent *e ) override;
45+
virtual void canvasReleaseEvent( QgsMapMouseEvent *e ) override;
46+
virtual void activate() override;
47+
virtual void deactivate() override;
48+
49+
/** Sets a fixed aspect ratio to be used when dragging extent onto the canvas.
50+
* To unset a fixed aspect ratio, set the width and height to zero.
51+
* \param ratio aspect ratio's width and height
52+
* */
53+
void setRatio( QSize ratio ) { mRatio = ratio; }
54+
55+
/** Returns the current fixed aspect ratio to be used when dragging extent onto the canvas.
56+
* If the aspect ratio isn't fixed, the width and height will be set to zero.
57+
* */
58+
QSize ratio() const { return mRatio; }
59+
60+
/** Returns the current extent drawn onto the canvas.
61+
*/
62+
QgsRectangle extent() const;
63+
64+
signals:
65+
66+
//! signal emitted on extent change
67+
void extentChanged( const QgsRectangle &extent );
68+
69+
private:
70+
71+
void calculateEndPoint( QgsPointXY &point );
72+
73+
void drawExtent();
74+
75+
std::unique_ptr< QgsRubberBand > mRubberBand;
76+
77+
QgsPointXY mStartPoint;
78+
QgsPointXY mEndPoint;
79+
80+
bool mDraw = false;
81+
82+
QSize mRatio;
83+
84+
};
85+
86+
#endif

‎src/ui/qgsextentgroupboxwidget.ui

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,19 @@
155155
</property>
156156
</widget>
157157
</item>
158+
<item row="1" column="4">
159+
<widget class="QPushButton" name="mButtonDrawOnCanvas">
160+
<property name="sizePolicy">
161+
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
162+
<horstretch>0</horstretch>
163+
<verstretch>0</verstretch>
164+
</sizepolicy>
165+
</property>
166+
<property name="text">
167+
<string>Draw on canvas</string>
168+
</property>
169+
</widget>
170+
</item>
158171
</layout>
159172
</widget>
160173
</item>

0 commit comments

Comments
 (0)
Please sign in to comment.