Skip to content

Commit 9e14741

Browse files
committed
Move logic for maintaining exact extent when CRS changes from
raster layer save as dialog to QgsExtentGroupBox, add tests
1 parent af029c5 commit 9e14741

File tree

5 files changed

+112
-51
lines changed

5 files changed

+112
-51
lines changed

python/gui/qgsextentgroupbox.sip

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,34 @@ class QgsExtentGroupBox : QgsCollapsibleGroupBox
6767
void setCurrentExtent( const QgsRectangle &currentExtent, const QgsCoordinateReferenceSystem &currentCrs );
6868
%Docstring
6969
Sets the current extent to show in the widget - should be called as part of initialization (or whenever current extent changes).
70+
The current extent is usually set to match the current map canvas extent.
7071
.. seealso:: currentExtent()
7172
.. seealso:: currentCrs()
7273
%End
7374

7475
QgsRectangle currentExtent() const;
7576
%Docstring
76-
Returns the current extent set for the widget.
77+
Returns the current extent set for the widget. The current extent is usually set to match the
78+
current map canvas extent.
7779
.. seealso:: setCurrentExtent()
7880
.. seealso:: currentCrs()
7981
:rtype: QgsRectangle
8082
%End
8183

8284
QgsCoordinateReferenceSystem currentCrs() const;
8385
%Docstring
84-
Returns the coordinate reference system for the current extent set for the widget.
86+
Returns the coordinate reference system for the current extent set for the widget. The current
87+
extent and CRS usually reflects the map canvas extent and CRS.
8588
.. seealso:: setCurrentExtent()
8689
.. seealso:: currentExtent()
8790
:rtype: QgsCoordinateReferenceSystem
8891
%End
8992

90-
void setOutputCrs( const QgsCoordinateReferenceSystem &outputCrs, bool reprojectCurrentExtent = true );
93+
void setOutputCrs( const QgsCoordinateReferenceSystem &outputCrs );
9194
%Docstring
9295
Sets the output CRS - may need to be used for transformation from original/current extent.
9396
Should be called as part of initialization and whenever the the output CRS is changed.
94-
If ``reprojectCurrentExtent`` is true then the current extent will be reproject into the
95-
new output CRS.
97+
The current extent will be reprojected into the new output CRS.
9698
%End
9799

98100
QgsRectangle outputExtent() const;

src/gui/qgsextentgroupbox.cpp

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,45 @@ void QgsExtentGroupBox::setCurrentExtent( const QgsRectangle &currentExtent, con
6262
mCurrentCrs = currentCrs;
6363
}
6464

65-
void QgsExtentGroupBox::setOutputCrs( const QgsCoordinateReferenceSystem &outputCrs, bool reprojectCurrentExtent )
65+
void QgsExtentGroupBox::setOutputCrs( const QgsCoordinateReferenceSystem &outputCrs )
6666
{
67-
if ( reprojectCurrentExtent && mOutputCrs != outputCrs )
67+
if ( mOutputCrs != outputCrs )
6868
{
69-
try
69+
switch ( mExtentState )
7070
{
71-
QgsCoordinateTransform ct( mOutputCrs, outputCrs );
72-
QgsRectangle extent = ct.transformBoundingBox( outputExtent() );
73-
mXMinLineEdit->setText( QgsRasterBlock::printValue( extent.xMinimum() ) );
74-
mXMaxLineEdit->setText( QgsRasterBlock::printValue( extent.xMaximum() ) );
75-
mYMinLineEdit->setText( QgsRasterBlock::printValue( extent.yMinimum() ) );
76-
mYMaxLineEdit->setText( QgsRasterBlock::printValue( extent.yMaximum() ) );
77-
}
78-
catch ( QgsCsException & )
79-
{
80-
// can't reproject
71+
case CurrentExtent:
72+
mOutputCrs = outputCrs;
73+
setOutputExtentFromCurrent();
74+
break;
75+
76+
case OriginalExtent:
77+
mOutputCrs = outputCrs;
78+
setOutputExtentFromOriginal();
79+
break;
80+
81+
case ProjectLayerExtent:
82+
mOutputCrs = outputCrs;
83+
setOutputExtentFromLayer( mExtentLayer.data() );
84+
break;
85+
86+
case UserExtent:
87+
try
88+
{
89+
QgsCoordinateTransform ct( mOutputCrs, outputCrs );
90+
QgsRectangle extent = ct.transformBoundingBox( outputExtent() );
91+
mOutputCrs = outputCrs;
92+
setOutputExtentFromUser( extent, outputCrs );
93+
}
94+
catch ( QgsCsException & )
95+
{
96+
// can't reproject
97+
mOutputCrs = outputCrs;
98+
}
99+
break;
81100
}
101+
82102
}
83-
mOutputCrs = outputCrs;
103+
84104
}
85105

86106
void QgsExtentGroupBox::setOutputExtent( const QgsRectangle &r, const QgsCoordinateReferenceSystem &srcCrs, ExtentState state )
@@ -168,7 +188,7 @@ void QgsExtentGroupBox::layerMenuAboutToShow()
168188
QAction *act = new QAction( icon, text, mLayerMenu );
169189
act->setToolTip( mMapLayerModel->data( index, Qt::ToolTipRole ).toString() );
170190
QString layerId = mMapLayerModel->data( index, QgsMapLayerModel::LayerIdRole ).toString();
171-
if ( mExtentState == ProjectLayerExtent && mExtentLayerId == layerId )
191+
if ( mExtentState == ProjectLayerExtent && mExtentLayer && mExtentLayer->id() == layerId )
172192
{
173193
act->setCheckable( true );
174194
act->setChecked( true );
@@ -212,7 +232,7 @@ void QgsExtentGroupBox::setOutputExtentFromLayer( const QgsMapLayer *layer )
212232
if ( !layer )
213233
return;
214234

215-
mExtentLayerId = layer->id();
235+
mExtentLayer = layer;
216236
mExtentLayerName = layer->name();
217237

218238
setOutputExtent( layer->extent(), layer->crs(), ProjectLayerExtent );

src/gui/qgsextentgroupbox.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,23 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
8282

8383
/**
8484
* Sets the current extent to show in the widget - should be called as part of initialization (or whenever current extent changes).
85+
* The current extent is usually set to match the current map canvas extent.
8586
* \see currentExtent()
8687
* \see currentCrs()
8788
*/
8889
void setCurrentExtent( const QgsRectangle &currentExtent, const QgsCoordinateReferenceSystem &currentCrs );
8990

9091
/**
91-
* Returns the current extent set for the widget.
92+
* Returns the current extent set for the widget. The current extent is usually set to match the
93+
* current map canvas extent.
9294
* \see setCurrentExtent()
9395
* \see currentCrs()
9496
*/
9597
QgsRectangle currentExtent() const { return mCurrentExtent; }
9698

9799
/**
98-
* Returns the coordinate reference system for the current extent set for the widget.
100+
* Returns the coordinate reference system for the current extent set for the widget. The current
101+
* extent and CRS usually reflects the map canvas extent and CRS.
99102
* \see setCurrentExtent()
100103
* \see currentExtent()
101104
*/
@@ -104,10 +107,9 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
104107
/**
105108
* Sets the output CRS - may need to be used for transformation from original/current extent.
106109
* Should be called as part of initialization and whenever the the output CRS is changed.
107-
* If \a reprojectCurrentExtent is true then the current extent will be reproject into the
108-
* new output CRS.
110+
* The current extent will be reprojected into the new output CRS.
109111
*/
110-
void setOutputCrs( const QgsCoordinateReferenceSystem &outputCrs, bool reprojectCurrentExtent = true );
112+
void setOutputCrs( const QgsCoordinateReferenceSystem &outputCrs );
111113

112114
/**
113115
* Returns the extent shown in the widget - in output CRS coordinates.
@@ -194,7 +196,7 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
194196
QMenu *mLayerMenu = nullptr;
195197
QgsMapLayerModel *mMapLayerModel = nullptr;
196198
QList< QAction * > mMenuActions;
197-
QString mExtentLayerId;
199+
QPointer< const QgsMapLayer > mExtentLayer;
198200
QString mExtentLayerName;
199201

200202
void setExtentToLayerExtent( const QString &layerId );

src/gui/qgsrasterlayersaveasdialog.cpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -435,24 +435,7 @@ void QgsRasterLayerSaveAsDialog::crsChanged()
435435
{
436436
if ( outputCrs() != mPreviousCrs )
437437
{
438-
mExtentGroupBox->setOutputCrs( outputCrs(), false );
439-
QgsExtentGroupBox::ExtentState state = mExtentGroupBox->extentState();
440-
441-
// Reset extent
442-
// We could reproject previous but that would add additional space also if
443-
// it is was not necessary or at leas it could decrease accuracy
444-
if ( state == QgsExtentGroupBox::OriginalExtent )
445-
{
446-
mExtentGroupBox->setOutputExtentFromOriginal();
447-
}
448-
else if ( state == QgsExtentGroupBox::CurrentExtent )
449-
{
450-
mExtentGroupBox->setOutputExtentFromCurrent();
451-
}
452-
else
453-
{
454-
mExtentGroupBox->setOutputExtentFromUser( mExtentGroupBox->outputExtent(), mPreviousCrs );
455-
}
438+
mExtentGroupBox->setOutputCrs( outputCrs() );
456439

457440
// Reset resolution
458441
if ( mResolutionRadioButton->isChecked() )

tests/src/python/test_qgsextentgroupbox.py

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import qgis # NOQA
1616
import os
1717

18-
from qgis.core import QgsRectangle, QgsCoordinateReferenceSystem, QgsVectorLayer, QgsProject
18+
from qgis.core import QgsRectangle, QgsCoordinateReferenceSystem, QgsVectorLayer, QgsProject, QgsFeature, QgsGeometry
1919
from qgis.gui import QgsExtentGroupBox
2020

2121
from qgis.PyQt.QtTest import QSignalSpy
@@ -82,6 +82,8 @@ def test_SettingExtent(self):
8282
self.assertEqual(w.extentState(), QgsExtentGroupBox.ProjectLayerExtent)
8383
self.assertEqual(len(spy), 4)
8484

85+
QgsProject.instance().removeAllMapLayers()
86+
8587
def testSetOutputCrs(self):
8688
w = qgis.gui.QgsExtentGroupBox()
8789

@@ -90,14 +92,66 @@ def testSetOutputCrs(self):
9092
w.setOutputExtentFromCurrent()
9193
self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4))
9294

93-
# no reprojection
94-
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785'), False)
95+
# with reprojection
96+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785'))
97+
self.assertEqual(w.outputExtent().toString(4), QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4))
98+
# change CRS back
99+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
100+
# extent should be back to current - not a reprojection of the reprojected bounds
101+
self.assertEqual(w.outputExtent().toString(20), QgsRectangle(1, 2, 3, 4).toString(20))
102+
103+
# repeat, this time using original extents
104+
w = qgis.gui.QgsExtentGroupBox()
105+
106+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
107+
w.setOriginalExtent(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('epsg:4326'))
108+
w.setOutputExtentFromOriginal()
95109
self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4))
96-
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'), False)
97110

98111
# with reprojection
99-
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785'), True)
100-
self.assertEqual(w.outputExtent().toString(4), QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4))
112+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785'))
113+
self.assertEqual(w.outputExtent().toString(4),
114+
QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4))
115+
# change CRS back
116+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
117+
# extent should be back to original - not a reprojection of the reprojected bounds
118+
self.assertEqual(w.outputExtent().toString(20), QgsRectangle(1, 2, 3, 4).toString(20))
119+
120+
# repeat, this time using layer extent
121+
layer = QgsVectorLayer("Polygon?crs=4326", 'memory', 'memory')
122+
self.assertTrue(layer.isValid())
123+
f = QgsFeature()
124+
f.setGeometry(QgsGeometry.fromWkt('Polygon((1 2, 3 2, 3 4, 1 4, 1 2))'))
125+
layer.dataProvider().addFeatures([f])
126+
QgsProject.instance().addMapLayer(layer)
127+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
128+
w.setOutputExtentFromLayer(layer)
129+
self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4))
130+
131+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785'))
132+
self.assertEqual(w.outputExtent().toString(4),
133+
QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4))
134+
# change CRS back
135+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
136+
# extent should be back to original - not a reprojection of the reprojected bounds
137+
self.assertEqual(w.outputExtent().toString(20), QgsRectangle(1, 2, 3, 4).toString(20))
138+
139+
# custom extent
140+
w = qgis.gui.QgsExtentGroupBox()
141+
142+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
143+
w.setOutputExtentFromUser(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('epsg:4326'))
144+
self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4))
145+
146+
# with reprojection
147+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785'))
148+
self.assertEqual(w.outputExtent().toString(4),
149+
QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4))
150+
# change CRS back
151+
w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326'))
152+
# in this case we can't retrieve the original user extent in 4326, so we have a reprojection of the reprojected bounds
153+
# just test this by restricting the test to 4 decimals
154+
self.assertEqual(w.outputExtent().toString(4), QgsRectangle(1, 2, 3, 4).toString(4))
101155

102156

103157
if __name__ == '__main__':

0 commit comments

Comments
 (0)