Skip to content
Permalink
Browse files

Rework sync extent option for map views

Instead of a single sync extent option, the option has been split
into a "sync map center" option, and a "sync scale" option.

Sync center just does that - syncs the center of the map without
changing the scale. This allows you to have an overview style
or magnified map which follows the main canvas center.

Sync scale borrows from the Auxillary Window/Dockable Mirror
Map plugin approach. If sync scale is enabled, a "scale factor"
is utilised to multiply the main canvas scale. This allows
you to have a view which is e.g. always 2x the scale of the
main canvas.

Splitting the sync extent option like this allows us to address
a much wider set of use cases with map views (at the cost of
a bit of UI complexity). It also helps cover more of the
feature set from the Aux Window/Dockable Mirror Map plugins,
hopefully allowing this built-in view approach to make the need
for 3rd party plugins redundant.
  • Loading branch information
nyalldawson committed Mar 14, 2017
1 parent e21b908 commit 57637ca81c5c44e9d03a473811ac74c021d71ce4
@@ -3113,7 +3113,7 @@ QgsMapCanvas *QgisApp::mapCanvas()
return mMapCanvas;
}

QgsMapCanvas *QgisApp::createNewMapCanvas( const QString &name, bool isFloating, const QRect &dockGeometry, bool synced, bool showCursor )
QgsMapCanvas *QgisApp::createNewMapCanvas( const QString &name, bool isFloating, const QRect &dockGeometry, bool synced, bool showCursor, bool scaleSynced, double scaleFactor )
{
Q_FOREACH ( QgsMapCanvas *canvas, mapCanvases() )
{
@@ -3168,8 +3168,10 @@ QgsMapCanvas *QgisApp::createNewMapCanvas( const QString &name, bool isFloating,
connect( mapCanvasWidget, &QgsMapCanvasDockWidget::closed, this, &QgisApp::markDirty );
connect( mapCanvasWidget, &QgsMapCanvasDockWidget::renameTriggered, this, &QgisApp::renameView );

mapCanvasWidget->setViewExtentSynchronized( synced );
mapCanvasWidget->setViewCenterSynchronized( synced );
mapCanvasWidget->setCursorMarkerVisible( showCursor );
mapCanvasWidget->setScaleFactor( scaleFactor );
mapCanvasWidget->setViewScaleSynchronized( scaleSynced );

return mapCanvas;
}
@@ -11753,8 +11755,10 @@ void QgisApp::writeProject( QDomDocument &doc )
node.setAttribute( QStringLiteral( "width" ), w->width() );
node.setAttribute( QStringLiteral( "height" ), w->height() );
node.setAttribute( QStringLiteral( "floating" ), w->isFloating() );
node.setAttribute( QStringLiteral( "synced" ), w->isViewExtentSynchronized() );
node.setAttribute( QStringLiteral( "synced" ), w->isViewCenterSynchronized() );
node.setAttribute( QStringLiteral( "showCursor" ), w->isCursorMarkerVisible() );
node.setAttribute( QStringLiteral( "scaleSynced" ), w->isViewScaleSynchronized() );
node.setAttribute( QStringLiteral( "scaleFactor" ), w->scaleFactor() );
mapViewNode.appendChild( node );
}
qgisNode.appendChild( mapViewNode );
@@ -11791,8 +11795,10 @@ void QgisApp::readProject( const QDomDocument &doc )
bool floating = elementNode.attribute( QStringLiteral( "floating" ), QStringLiteral( "0" ) ).toInt();
bool synced = elementNode.attribute( QStringLiteral( "synced" ), QStringLiteral( "0" ) ).toInt();
bool showCursor = elementNode.attribute( QStringLiteral( "showCursor" ), QStringLiteral( "0" ) ).toInt();
bool scaleSynced = elementNode.attribute( QStringLiteral( "scaleSynced" ), QStringLiteral( "0" ) ).toInt();
double scaleFactor = elementNode.attribute( QStringLiteral( "scaleFactor" ), QStringLiteral( "1" ) ).toDouble();

QgsMapCanvas *mapCanvas = createNewMapCanvas( mapName, floating, QRect( x, y, w, h ), synced, showCursor );
QgsMapCanvas *mapCanvas = createNewMapCanvas( mapName, floating, QRect( x, y, w, h ), synced, showCursor, scaleSynced, scaleFactor );
mapCanvas->readProject( doc );
}
}
@@ -242,7 +242,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
* and widget geometry rect for the dock.
*/
QgsMapCanvas *createNewMapCanvas( const QString &name, bool isFloating = false, const QRect &dockGeometry = QRect(),
bool synced = false, bool showCursor = true );
bool synced = false, bool showCursor = true, bool scaleSynced = false,
double scaleFactor = 1.0 );

/**
* Closes any additional map canvases. The main map canvas will not
@@ -57,10 +57,9 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa

mMainWidget->layout()->addWidget( mMapCanvas );

connect( mActionSyncView, &QAction::toggled, this, [ = ]( bool active )
connect( mActionSyncView, &QAction::toggled, this, [ = ]
{
syncViewExtent( mMainCanvas );
syncView( active );
syncViewCenter( mMainCanvas );
} );

mMenu = new QMenu();
@@ -110,6 +109,9 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa
mScaleCombo = settingsAction->scaleCombo();
mRotationEdit = settingsAction->rotationSpinBox();
mMagnificationEdit = settingsAction->magnifierSpinBox();
mSyncScaleCheckBox = settingsAction->syncScaleCheckBox();
mScaleFactorWidget = settingsAction->scaleFactorSpinBox();

connect( mScaleCombo, &QgsScaleComboBox::scaleChanged, this, [ = ]( double scale )
{
if ( !mBlockScaleUpdate )
@@ -171,11 +173,19 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa
}
} );

connect( mScaleFactorWidget, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsMapCanvasDockWidget::mapScaleChanged );
connect( mSyncScaleCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
{
if ( checked )
mapScaleChanged();
}
);

mResizeTimer.setSingleShot( true );
connect( &mResizeTimer, &QTimer::timeout, this, [ = ]
{
mBlockExtentSync = false;
syncViewExtent( mMainCanvas );
syncViewCenter( mMainCanvas );
} );
}

@@ -184,23 +194,28 @@ void QgsMapCanvasDockWidget::setMainCanvas( QgsMapCanvas *canvas )
if ( mMainCanvas )
{
disconnect( mMainCanvas, &QgsMapCanvas::xyCoordinates, this, &QgsMapCanvasDockWidget::syncMarker );
disconnect( mMainCanvas, &QgsMapCanvas::scaleChanged, this, &QgsMapCanvasDockWidget::mapScaleChanged );
disconnect( mMainCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged );
}

mMainCanvas = canvas;
connect( mMainCanvas, &QgsMapCanvas::xyCoordinates, this, &QgsMapCanvasDockWidget::syncMarker );
connect( mMainCanvas, &QgsMapCanvas::scaleChanged, this, &QgsMapCanvasDockWidget::mapScaleChanged );
connect( mMainCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged );
connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged, Qt::UniqueConnection );
}

QgsMapCanvas *QgsMapCanvasDockWidget::mapCanvas()
{
return mMapCanvas;
}

void QgsMapCanvasDockWidget::setViewExtentSynchronized( bool enabled )
void QgsMapCanvasDockWidget::setViewCenterSynchronized( bool enabled )
{
mActionSyncView->setChecked( enabled );
}

bool QgsMapCanvasDockWidget::isViewExtentSynchronized() const
bool QgsMapCanvasDockWidget::isViewCenterSynchronized() const
{
return mActionSyncView->isChecked();
}
@@ -215,6 +230,26 @@ bool QgsMapCanvasDockWidget::isCursorMarkerVisible() const
return mXyMarker->isVisible();
}

void QgsMapCanvasDockWidget::setScaleFactor( double factor )
{
mScaleFactorWidget->setValue( factor );
}

void QgsMapCanvasDockWidget::setViewScaleSynchronized( bool enabled )
{
mSyncScaleCheckBox->setChecked( enabled );
}

bool QgsMapCanvasDockWidget::isViewScaleSynchronized() const
{
return mSyncScaleCheckBox->isChecked();
}

double QgsMapCanvasDockWidget::scaleFactor() const
{
return mScaleFactorWidget->value();
}

void QgsMapCanvasDockWidget::resizeEvent( QResizeEvent * )
{
mBlockExtentSync = true;
@@ -233,21 +268,7 @@ void QgsMapCanvasDockWidget::setMapCrs()
}
}

void QgsMapCanvasDockWidget::syncView( bool enabled )
{
if ( enabled )
{
connect( mMainCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged, Qt::UniqueConnection );
connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged, Qt::UniqueConnection );
}
else
{
disconnect( mMainCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged );
disconnect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapCanvasDockWidget::mapExtentChanged );
}
}

void QgsMapCanvasDockWidget::syncViewExtent( QgsMapCanvas *sourceCanvas )
void QgsMapCanvasDockWidget::syncViewCenter( QgsMapCanvas *sourceCanvas )
{
// avoid infinite recursion
mBlockExtentSync = true;
@@ -259,11 +280,11 @@ void QgsMapCanvasDockWidget::syncViewExtent( QgsMapCanvas *sourceCanvas )
destCanvas->mapSettings().destinationCrs() );
try
{
destCanvas->setExtent( ct.transformBoundingBox( sourceCanvas->extent() ) );
destCanvas->setCenter( ct.transform( sourceCanvas->center() ) );
}
catch ( QgsCsException & )
{
destCanvas->setExtent( sourceCanvas->extent() );
destCanvas->setCenter( sourceCanvas->center() );
}
destCanvas->refresh();

@@ -279,7 +300,14 @@ void QgsMapCanvasDockWidget::mapExtentChanged()
if ( !sourceCanvas )
return;

syncViewExtent( sourceCanvas );
if ( sourceCanvas == mMapCanvas && mSyncScaleCheckBox->isChecked() )
{
double newScaleFactor = mMainCanvas->scale() / mMapCanvas->scale();
mScaleFactorWidget->setValue( newScaleFactor );
}

if ( mActionSyncView->isChecked() )
syncViewCenter( sourceCanvas );
}

void QgsMapCanvasDockWidget::mapCrsChanged()
@@ -351,6 +379,18 @@ void QgsMapCanvasDockWidget::syncMarker( const QgsPoint &p )
mXyMarker->setCenter( t );
}

void QgsMapCanvasDockWidget::mapScaleChanged()
{
if ( !mSyncScaleCheckBox->isChecked() )
return;

double newScale = mMainCanvas->scale() / mScaleFactorWidget->value();
bool prev = mBlockExtentSync;
mBlockExtentSync = true;
mMapCanvas->zoomScale( newScale );
mBlockExtentSync = prev;
}

QgsMapSettingsAction::QgsMapSettingsAction( QWidget *parent )
: QWidgetAction( parent )
{
@@ -397,6 +437,27 @@ QgsMapSettingsAction::QgsMapSettingsAction( QWidget *parent )
gLayout->addWidget( label, 2, 0 );
gLayout->addWidget( mMagnifierWidget, 2, 1 );

mSyncScaleCheckBox = new QCheckBox( tr( "Synchronize scale" ) );
gLayout->addWidget( mSyncScaleCheckBox, 3, 0, 1, 2 );

mScaleFactorWidget = new QgsDoubleSpinBox();
mScaleFactorWidget->setSuffix( QStringLiteral( "×" ) );
mScaleFactorWidget->setDecimals( 2 );
mScaleFactorWidget->setRange( 0.01, 100000 );
mScaleFactorWidget->setWrapping( false );
mScaleFactorWidget->setSingleStep( 0.1 );
mScaleFactorWidget->setToolTip( tr( "Multiplication factor for main canvas scale to view scale" ) );
mScaleFactorWidget->setClearValueMode( QgsDoubleSpinBox::CustomValue );
mScaleFactorWidget->setClearValue( 1.0 );
mScaleFactorWidget->setValue( 1.0 );
mScaleFactorWidget->setEnabled( false );

connect( mSyncScaleCheckBox, &QCheckBox::toggled, mScaleFactorWidget, &QgsDoubleSpinBox::setEnabled );

label = new QLabel( tr( "Scale factor" ) );
gLayout->addWidget( label, 4, 0 );
gLayout->addWidget( mScaleFactorWidget, 4, 1 );

QWidget *w = new QWidget();
w->setLayout( gLayout );
setDefaultWidget( w );
@@ -30,6 +30,7 @@ class QgsDoubleSpinBox;
class QgsStatusBarMagnifierWidget;
class QgsMapToolPan;
class QgsVertexMarker;
class QCheckBox;

/**
* \class QgsMapCanvasDockWidget
@@ -53,16 +54,16 @@ class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsM
QgsMapCanvas *mapCanvas();

/**
* Sets whether the view extent should be synchronized with the main canvas extent.
* @see isViewExtentSynchronized()
* Sets whether the view center should be synchronized with the main canvas center.
* @see isViewCenterSynchronized()
*/
void setViewExtentSynchronized( bool enabled );
void setViewCenterSynchronized( bool enabled );

/**
* Returns true if the view extent is synchronized with the main canvas extent.
* @see setViewExtentSynchronized()
* @see setViewCenterSynchronized()
*/
bool isViewExtentSynchronized() const;
bool isViewCenterSynchronized() const;

/**
* Sets whether the cursor position marker is visible.
@@ -76,6 +77,34 @@ class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsM
*/
bool isCursorMarkerVisible() const;

/**
* Returns the scaling factor for main canvas scale to view scale.
* @see setScaleFactor()
* @see isViewScaleSynchronized()
*/
double scaleFactor() const;

/**
* Sets the scaling \a factor for main canvas scale to view scale.
* @see scaleFactor()
* @see setViewScaleSynchronized()
*/
void setScaleFactor( double factor );

/**
* Sets whether the view scale should be synchronized with the main canvas center.
* @see isViewScaleSynchronized()
* @see setScaleFactor()
*/
void setViewScaleSynchronized( bool enabled );

/**
* Returns true if the view scale is synchronized with the main canvas extent.
* @see setViewScaleSynchronized()
* @see scaleFactor()
*/
bool isViewScaleSynchronized() const;

signals:

void renameTriggered();
@@ -87,12 +116,12 @@ class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsM
private slots:

void setMapCrs();
void syncView( bool enabled );
void mapExtentChanged();
void mapCrsChanged();
void menuAboutToShow();
void settingsMenuAboutToShow();
void syncMarker( const QgsPoint &p );
void mapScaleChanged();

private:

@@ -103,14 +132,16 @@ class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsM
QgsScaleComboBox *mScaleCombo = nullptr;
QgsDoubleSpinBox *mRotationEdit = nullptr;
QgsDoubleSpinBox *mMagnificationEdit = nullptr;
QgsDoubleSpinBox *mScaleFactorWidget = nullptr;
QCheckBox *mSyncScaleCheckBox = nullptr;
bool mBlockScaleUpdate = false;
bool mBlockRotationUpdate = false;
bool mBlockMagnificationUpdate = false;
bool mBlockExtentSync = false;
QgsMapToolPan *mPanTool = nullptr;
QTimer mResizeTimer;
QgsVertexMarker *mXyMarker = nullptr;
void syncViewExtent( QgsMapCanvas *sourceCanvas );
void syncViewCenter( QgsMapCanvas *sourceCanvas );
};

/**
@@ -130,11 +161,15 @@ class QgsMapSettingsAction: public QWidgetAction
QgsScaleComboBox *scaleCombo() { return mScaleCombo; }
QgsDoubleSpinBox *rotationSpinBox() { return mRotationWidget; }
QgsDoubleSpinBox *magnifierSpinBox() { return mMagnifierWidget; }
QgsDoubleSpinBox *scaleFactorSpinBox() { return mScaleFactorWidget; }
QCheckBox *syncScaleCheckBox() { return mSyncScaleCheckBox; }

private:
QgsScaleComboBox *mScaleCombo = nullptr;
QgsDoubleSpinBox *mRotationWidget = nullptr;
QgsDoubleSpinBox *mMagnifierWidget = nullptr;
QCheckBox *mSyncScaleCheckBox = nullptr;
QgsDoubleSpinBox *mScaleFactorWidget = nullptr;
};


@@ -94,7 +94,7 @@
<string>Synchronize View</string>
</property>
<property name="toolTip">
<string>Synchronize View with Main Map</string>
<string>Synchronize View Center with Main Map</string>
</property>
</action>
<action name="mActionRename">

0 comments on commit 57637ca

Please sign in to comment.
You can’t perform that action at this time.