Skip to content

Commit c9a2ead

Browse files
committedApr 2, 2023
Fix logic with secondary map canvases with both "sync extent"
and "sync scale" enabled Instead of this combination resulting in frequent unwanted changes to the secondary canvas "scale factor" setting, ensure that instead the desired effect of actually syncing both the canvas extent AND scale are always kept in sync.
1 parent d82b851 commit c9a2ead

File tree

4 files changed

+359
-2
lines changed

4 files changed

+359
-2
lines changed
 

‎src/app/qgsmapcanvasdockwidget.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,15 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa
226226
connect( &mResizeTimer, &QTimer::timeout, this, [ = ]
227227
{
228228
mBlockExtentSync = false;
229+
230+
if ( mSyncScaleCheckBox->isChecked() )
231+
{
232+
mBlockExtentSync = true;
233+
const double scale = mMapCanvas->scale();
234+
mMainCanvas->zoomScale( scale * mScaleFactorWidget->value() );
235+
mBlockExtentSync = false;
236+
}
237+
229238
if ( mSyncExtentCheck->isChecked() )
230239
syncViewCenter( mMainCanvas );
231240
} );
@@ -392,8 +401,10 @@ void QgsMapCanvasDockWidget::mapExtentChanged()
392401

393402
if ( sourceCanvas == mMapCanvas && mSyncScaleCheckBox->isChecked() )
394403
{
395-
const double newScaleFactor = mMainCanvas->scale() / mMapCanvas->scale();
396-
mScaleFactorWidget->setValue( newScaleFactor );
404+
mBlockExtentSync = true;
405+
const double scale = mMapCanvas->scale();
406+
mMainCanvas->zoomScale( scale * mScaleFactorWidget->value() );
407+
mBlockExtentSync = false;
397408
}
398409

399410
if ( mSyncExtentCheck->isChecked() )

‎src/app/qgsmapcanvasdockwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsM
190190
QgsRubberBand *mExtentRubberBand = nullptr;
191191
void syncViewCenter( QgsMapCanvas *sourceCanvas );
192192
void syncSelection();
193+
194+
friend class TestQgsMapCanvasDockWidget;
193195
};
194196

195197
/**

‎tests/src/app/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ set(TESTS
1818
testqgsapplocatorfilters.cpp
1919
testqgsdecorationscalebar.cpp
2020
testqgsfieldcalculator.cpp
21+
testqgsmapcanvasdockwidget.cpp
2122
testqgsmaptooleditannotation.cpp
2223
testqgsmaptoolidentifyaction.cpp
2324
testqgsmaptoollabel.cpp
Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
/***************************************************************************
2+
testqgsmapcanvasdockwidget.cpp
3+
--------------------------------------
4+
Date : March 2023
5+
Copyright : (C) 2023 by Nyall Dawson
6+
Email : nyall dot dawson 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+
#include "qgstest.h"
16+
17+
#include <QApplication>
18+
#include <QList>
19+
#include <QObject>
20+
#include <QString>
21+
#include <QStringList>
22+
#include <QDockWidget>
23+
#include <QSignalSpy>
24+
25+
#include "qgsapplication.h"
26+
#include "qgsmapcanvas.h"
27+
#include "qgsmapcanvasdockwidget.h"
28+
29+
/**
30+
* \ingroup UnitTests
31+
* This is a unit test for the app map canvas dock widgets.
32+
*/
33+
class TestQgsMapCanvasDockWidget : public QObject
34+
{
35+
Q_OBJECT
36+
37+
public:
38+
TestQgsMapCanvasDockWidget();
39+
40+
private slots:
41+
void initTestCase();// will be called before the first testfunction is executed.
42+
void cleanupTestCase();// will be called after the last testfunction was executed.
43+
void init() {}; // will be called before each testfunction is executed.
44+
void cleanup() {} // will be called after every testfunction.
45+
46+
void testNoSync();
47+
void testScaleSync();
48+
void testCenterSync();
49+
void testScaleAndCenterSync();
50+
51+
private:
52+
};
53+
54+
TestQgsMapCanvasDockWidget::TestQgsMapCanvasDockWidget() = default;
55+
56+
//runs before all tests
57+
void TestQgsMapCanvasDockWidget::initTestCase()
58+
{
59+
// Set up the QgsSettings environment
60+
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
61+
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
62+
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
63+
64+
QgsApplication::init();
65+
QgsApplication::initQgis();
66+
}
67+
68+
//runs after all tests
69+
void TestQgsMapCanvasDockWidget::cleanupTestCase()
70+
{
71+
QgsApplication::exitQgis();
72+
}
73+
74+
void TestQgsMapCanvasDockWidget::testNoSync()
75+
{
76+
// canvases should be completely independent
77+
QgsMapCanvas mainCanvas;
78+
mainCanvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
79+
mainCanvas.setFrameStyle( QFrame::NoFrame );
80+
mainCanvas.resize( 600, 600 );
81+
mainCanvas.setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
82+
mainCanvas.show();
83+
84+
QGSCOMPARENEAR( mainCanvas.scale(), 44823779, 10000 );
85+
86+
QgsMapCanvasDockWidget dock( QStringLiteral( "dock" ) );
87+
dock.setMainCanvas( &mainCanvas );
88+
dock.mapCanvas()->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
89+
dock.mapCanvas()->setFrameStyle( QFrame::NoFrame );
90+
dock.resize( 600, 600 );
91+
dock.mapCanvas()->setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
92+
dock.show();
93+
94+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 44823779, 1000000 );
95+
96+
dock.setViewCenterSynchronized( false );
97+
dock.setViewScaleSynchronized( false );
98+
99+
QSignalSpy resizeTimerSpy( &dock.mResizeTimer, &QTimer::timeout );
100+
resizeTimerSpy.wait();
101+
102+
mainCanvas.zoomScale( 89647558 );
103+
104+
// dock should not inherit scale
105+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 44823779, 1000000 );
106+
107+
// change scale in dock and check it is not synced to main canvas
108+
dock.mapCanvas()->zoomScale( 1500000 );
109+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1500000, 1000 );
110+
QGSCOMPARENEAR( mainCanvas.scale(), 89647558, 1000 );
111+
112+
// extent should NOT be synced
113+
dock.mapCanvas()->setCenter( QgsPointXY( -22329833, 3515327 ) );
114+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
115+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
116+
QGSCOMPARENEAR( mainCanvas.center().x(), -11281815, 1000 );
117+
QGSCOMPARENEAR( mainCanvas.center().y(), 4287781, 1000 );
118+
119+
mainCanvas.setCenter( QgsPointXY( -4467497, -227904 ) );
120+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
121+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
122+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
123+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
124+
}
125+
126+
void TestQgsMapCanvasDockWidget::testScaleSync()
127+
{
128+
QgsMapCanvas mainCanvas;
129+
mainCanvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
130+
mainCanvas.setFrameStyle( QFrame::NoFrame );
131+
mainCanvas.resize( 600, 600 );
132+
mainCanvas.setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
133+
mainCanvas.show();
134+
135+
QGSCOMPARENEAR( mainCanvas.scale(), 44823779, 10000 );
136+
137+
QgsMapCanvasDockWidget dock( QStringLiteral( "dock" ) );
138+
dock.setMainCanvas( &mainCanvas );
139+
dock.mapCanvas()->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
140+
dock.mapCanvas()->setFrameStyle( QFrame::NoFrame );
141+
dock.resize( 600, 600 );
142+
dock.mapCanvas()->setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
143+
dock.show();
144+
145+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 44823779, 1000000 );
146+
147+
dock.setViewCenterSynchronized( false );
148+
dock.setViewScaleSynchronized( true );
149+
dock.setScaleFactor( 1 );
150+
151+
QSignalSpy resizeTimerSpy( &dock.mResizeTimer, &QTimer::timeout );
152+
resizeTimerSpy.wait();
153+
154+
mainCanvas.zoomScale( 89647558 );
155+
156+
// dock should inherit scale
157+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 89647558, 1000000 );
158+
159+
// ensure scale is multiplied by factor
160+
dock.setScaleFactor( 2.5 );
161+
mainCanvas.zoomScale( 44823779 );
162+
163+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 17929511, 1000000 );
164+
165+
// change scale in dock and check it is respected by main canvas
166+
dock.setScaleFactor( 1.0 );
167+
dock.mapCanvas()->zoomScale( 1500000 );
168+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1500000, 1000 );
169+
QGSCOMPARENEAR( mainCanvas.scale(), 1500000, 1000 );
170+
171+
dock.setScaleFactor( 2.0 );
172+
dock.mapCanvas()->zoomScale( 1000000 );
173+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1000000, 1000 );
174+
QGSCOMPARENEAR( mainCanvas.scale(), 2000000, 1000 );
175+
176+
// extent should NOT be synced, and scale should not change when extent changes
177+
dock.mapCanvas()->setCenter( QgsPointXY( -22329833, 3515327 ) );
178+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
179+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
180+
QGSCOMPARENEAR( mainCanvas.center().x(), -11281815, 1000 );
181+
QGSCOMPARENEAR( mainCanvas.center().y(), 4287781, 1000 );
182+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1000000, 1000 );
183+
QGSCOMPARENEAR( mainCanvas.scale(), 2000000, 1000 );
184+
185+
mainCanvas.setCenter( QgsPointXY( -4467497, -227904 ) );
186+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
187+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
188+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
189+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
190+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1000000, 1000 );
191+
QGSCOMPARENEAR( mainCanvas.scale(), 2000000, 1000 );
192+
193+
dock.resize( 1200, 1200 );
194+
resizeTimerSpy.wait();
195+
196+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
197+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
198+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
199+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
200+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 500000, 10000 );
201+
QGSCOMPARENEAR( mainCanvas.scale(), 1000000, 100000 );
202+
}
203+
204+
void TestQgsMapCanvasDockWidget::testCenterSync()
205+
{
206+
// canvases should be completely independent
207+
QgsMapCanvas mainCanvas;
208+
mainCanvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
209+
mainCanvas.setFrameStyle( QFrame::NoFrame );
210+
mainCanvas.resize( 600, 600 );
211+
mainCanvas.setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
212+
mainCanvas.show();
213+
214+
QGSCOMPARENEAR( mainCanvas.scale(), 44823779, 10000 );
215+
216+
QgsMapCanvasDockWidget dock( QStringLiteral( "dock" ) );
217+
dock.setMainCanvas( &mainCanvas );
218+
dock.mapCanvas()->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
219+
dock.mapCanvas()->setFrameStyle( QFrame::NoFrame );
220+
dock.resize( 600, 600 );
221+
dock.mapCanvas()->setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
222+
dock.show();
223+
224+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 44823779, 1000000 );
225+
226+
dock.setViewCenterSynchronized( true );
227+
dock.setViewScaleSynchronized( false );
228+
229+
QSignalSpy resizeTimerSpy( &dock.mResizeTimer, &QTimer::timeout );
230+
resizeTimerSpy.wait();
231+
232+
mainCanvas.zoomScale( 89647558 );
233+
234+
// dock should not inherit scale
235+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 44823779, 1000000 );
236+
237+
// change scale in dock and check it is not synced to main canvas
238+
dock.mapCanvas()->zoomScale( 1500000 );
239+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1500000, 1000 );
240+
QGSCOMPARENEAR( mainCanvas.scale(), 89647558, 1000 );
241+
242+
// center SHOULD be synced
243+
dock.mapCanvas()->setCenter( QgsPointXY( -22329833, 3515327 ) );
244+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
245+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
246+
QGSCOMPARENEAR( mainCanvas.center().x(), -22329833, 1000 );
247+
QGSCOMPARENEAR( mainCanvas.center().y(), 3515327, 1000 );
248+
249+
mainCanvas.setCenter( QgsPointXY( -4467497, -227904 ) );
250+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -4467497, 1000 );
251+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), -227904, 1000 );
252+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
253+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
254+
255+
dock.resize( 1200, 1200 );
256+
resizeTimerSpy.wait();
257+
258+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -4467497, 1000 );
259+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), -227904, 1000 );
260+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
261+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
262+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 744966, 10000 );
263+
QGSCOMPARENEAR( mainCanvas.scale(), 89647558, 1000 );
264+
}
265+
266+
void TestQgsMapCanvasDockWidget::testScaleAndCenterSync()
267+
{
268+
// canvases should be completely independent
269+
QgsMapCanvas mainCanvas;
270+
mainCanvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
271+
mainCanvas.setFrameStyle( QFrame::NoFrame );
272+
mainCanvas.resize( 600, 600 );
273+
mainCanvas.setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
274+
mainCanvas.show();
275+
276+
QGSCOMPARENEAR( mainCanvas.scale(), 44823779, 10000 );
277+
278+
QgsMapCanvasDockWidget dock( QStringLiteral( "dock" ) );
279+
dock.setMainCanvas( &mainCanvas );
280+
dock.mapCanvas()->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
281+
dock.mapCanvas()->setFrameStyle( QFrame::NoFrame );
282+
dock.resize( 600, 600 );
283+
dock.mapCanvas()->setExtent( QgsRectangle( -14839703, 2282029, -7723928, 6293534 ) );
284+
dock.show();
285+
286+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 44823779, 1000000 );
287+
288+
dock.setViewCenterSynchronized( true );
289+
dock.setViewScaleSynchronized( true );
290+
291+
QSignalSpy resizeTimerSpy( &dock.mResizeTimer, &QTimer::timeout );
292+
resizeTimerSpy.wait();
293+
294+
mainCanvas.zoomScale( 89647558 );
295+
296+
// dock should inherit scale
297+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 89647558, 1000000 );
298+
299+
// ensure scale is multiplied by factor
300+
dock.setScaleFactor( 2.5 );
301+
mainCanvas.zoomScale( 44823779 );
302+
303+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 17929511, 1000000 );
304+
305+
// change scale in dock and check it is respected by main canvas
306+
dock.setScaleFactor( 1.0 );
307+
dock.mapCanvas()->zoomScale( 1500000 );
308+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1500000, 1000 );
309+
QGSCOMPARENEAR( mainCanvas.scale(), 1500000, 1000 );
310+
311+
dock.setScaleFactor( 2.0 );
312+
dock.mapCanvas()->zoomScale( 1000000 );
313+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 1000000, 1000 );
314+
QGSCOMPARENEAR( mainCanvas.scale(), 2000000, 1000 );
315+
316+
// center SHOULD be synced
317+
dock.mapCanvas()->setCenter( QgsPointXY( -22329833, 3515327 ) );
318+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -22329833, 1000 );
319+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), 3515327, 1000 );
320+
QGSCOMPARENEAR( mainCanvas.center().x(), -22329833, 1000 );
321+
QGSCOMPARENEAR( mainCanvas.center().y(), 3515327, 1000 );
322+
323+
mainCanvas.setCenter( QgsPointXY( -4467497, -227904 ) );
324+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -4467497, 1000 );
325+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), -227904, 1000 );
326+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
327+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
328+
329+
dock.resize( 1200, 1200 );
330+
resizeTimerSpy.wait();
331+
332+
QGSCOMPARENEAR( dock.mapCanvas()->center().x(), -4467497, 1000 );
333+
QGSCOMPARENEAR( dock.mapCanvas()->center().y(), -227904, 1000 );
334+
QGSCOMPARENEAR( mainCanvas.center().x(), -4467497, 1000 );
335+
QGSCOMPARENEAR( mainCanvas.center().y(), -227904, 1000 );
336+
QGSCOMPARENEAR( dock.mapCanvas()->scale(), 500000, 10000 );
337+
QGSCOMPARENEAR( mainCanvas.scale(), 1000000, 100000 );
338+
}
339+
340+
341+
342+
QGSTEST_MAIN( TestQgsMapCanvasDockWidget )
343+
#include "testqgsmapcanvasdockwidget.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.