Skip to content

Commit

Permalink
Rework sync extent option for map views
Browse files Browse the repository at this point in the history
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 57637ca
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 37 deletions.
14 changes: 10 additions & 4 deletions src/app/qgisapp.cpp
Expand Up @@ -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() )
{
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/app/qgisapp.h
Expand Up @@ -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
Expand Down
109 changes: 85 additions & 24 deletions src/app/qgsmapcanvasdockwidget.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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 );
} );
}

Expand All @@ -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();
}
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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();

Expand All @@ -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()
Expand Down Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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 );
Expand Down
49 changes: 42 additions & 7 deletions src/app/qgsmapcanvasdockwidget.h
Expand Up @@ -30,6 +30,7 @@ class QgsDoubleSpinBox;
class QgsStatusBarMagnifierWidget;
class QgsMapToolPan;
class QgsVertexMarker;
class QCheckBox;

/**
* \class QgsMapCanvasDockWidget
Expand All @@ -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.
Expand All @@ -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();
Expand All @@ -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:

Expand All @@ -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 );
};

/**
Expand All @@ -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;
};


Expand Down
2 changes: 1 addition & 1 deletion src/ui/qgsmapcanvasdockwidgetbase.ui
Expand Up @@ -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">
Expand Down

0 comments on commit 57637ca

Please sign in to comment.