Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] New "Save as image" settings dialog (#4390)
* Resolution dpi setting
* Extent setting
* Scale setting
* Draw annotations / decorations setting
  • Loading branch information
nirvn committed Apr 23, 2017
1 parent 4a2226a commit 268acab
Show file tree
Hide file tree
Showing 9 changed files with 439 additions and 7 deletions.
5 changes: 5 additions & 0 deletions python/core/qgsmaprenderertask.sip
Expand Up @@ -47,6 +47,11 @@ class QgsMapRendererTask : QgsTask
Adds ``annotations`` to be rendered on the map.
%End

void addDecorations( QList< QgsMapDecoration * > decorations );
%Docstring
Adds ``decorations`` to be rendered on the map.
%End

virtual void cancel();


Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -50,6 +50,7 @@ SET(QGIS_APP_SRCS
qgsloadstylefromdbdialog.cpp
qgsmapcanvasdockwidget.cpp
qgsmaplayerstyleguiutils.cpp
qgsmapsavedialog.cpp
qgsrulebasedlabelingwidget.cpp
qgssavestyletodbdialog.cpp
qgssnappinglayertreemodel.cpp
Expand Down Expand Up @@ -230,6 +231,7 @@ SET (QGIS_APP_MOC_HDRS
qgsloadstylefromdbdialog.h
qgsmapcanvasdockwidget.h
qgsmaplayerstyleguiutils.h
qgsmapsavedialog.h
qgsrulebasedlabelingwidget.h
qgssavestyletodbdialog.h
qgssnappinglayertreemodel.h
Expand Down
47 changes: 40 additions & 7 deletions src/app/qgisapp.cpp
Expand Up @@ -272,7 +272,9 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgsvectorlayerutils.h"
#include "qgshelp.h"
#include "qgsvectorfilewritertask.h"
#include "qgsmapsavedialog.h"
#include "qgsmaprenderertask.h"
#include "qgsmapdecoration.h"
#include "qgsnewnamedialog.h"

#include "qgssublayersdialog.h"
Expand Down Expand Up @@ -5781,24 +5783,55 @@ void QgisApp::updateFilterLegend()

void QgisApp::saveMapAsImage()
{
QList< QgsMapDecoration * > decorations;
QString activeDecorations;
Q_FOREACH ( QgsDecorationItem *decoration, mDecorationItems )
{
if ( decoration->enabled() )
{
decorations << decoration;
if ( activeDecorations.isEmpty() )
activeDecorations = decoration->name().toLower();
else
activeDecorations += QString( ", %1" ).arg( decoration->name().toLower() );
}
}

QgsMapSaveDialog dlg( this, mMapCanvas, activeDecorations );
if ( !dlg.exec() )
return;

QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
if ( myFileNameAndFilter.first != QLatin1String( "" ) )
{
//TODO: GUI
int dpi = qt_defaultDpiX();
QSize size = mMapCanvas->size() * ( dpi / qt_defaultDpiX() );
QSize size = mMapCanvas->size();
if ( dlg.extent() != mMapCanvas->extent() )
{
size.setWidth( mMapCanvas->size().width() * dlg.extent().width() / mMapCanvas->extent().width() );
size.setHeight( mMapCanvas->size().height() * dlg.extent().height() / mMapCanvas->extent().height() );
}
size *= dlg.dpi() / qt_defaultDpiX();

QgsMapSettings ms = QgsMapSettings();
ms.setDestinationCrs( QgsProject::instance()->crs() );
ms.setExtent( mMapCanvas->extent() );
ms.setOutputSize( size );
ms.setOutputDpi( dpi );
ms.setExtent( dlg.extent() );
ms.setOutputSize( dlg.size() );
ms.setOutputDpi( dlg.dpi() );
ms.setBackgroundColor( mMapCanvas->canvasColor() );
ms.setRotation( mMapCanvas->rotation() );
ms.setLayers( mMapCanvas->layers() );

QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, myFileNameAndFilter.first, myFileNameAndFilter.second );
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );

if ( dlg.drawAnnotations() )
{
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );
}

if ( dlg.drawDecorations() )
{
mapRendererTask->addDecorations( decorations );
}

connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ = ]
{
Expand Down
117 changes: 117 additions & 0 deletions src/app/qgsmapsavedialog.cpp
@@ -0,0 +1,117 @@
/***************************************************************************
qgsmapsavedialog.cpp
-------------------------------------
begin : April 2017
copyright : (C) 2017 by Mathieu Pellerin
email : nirvn dot asia at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsmapsavedialog.h"

#include "qgis.h"
#include "qgsscalecalculator.h"
#include "qgsdecorationitem.h"
#include "qgsextentgroupbox.h"
#include "qgsmapsettings.h"

#include <QCheckBox>
#include <QSpinBox>
#include <QList>

Q_GUI_EXPORT extern int qt_defaultDpiX();

QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, const QString &activeDecorations )
: QDialog( parent )
{
setupUi( this );

mExtent = mapCanvas->mapSettings().visibleExtent();
mDpi = mapCanvas->mapSettings().outputDpi();
mSize = mapCanvas->mapSettings().outputSize();

mResolutionSpinBox->setValue( qt_defaultDpiX() );

mExtentGroupBox->setOutputCrs( mapCanvas->mapSettings().destinationCrs() );
mExtentGroupBox->setCurrentExtent( mapCanvas->mapSettings().visibleExtent(), mapCanvas->mapSettings().destinationCrs() );
mExtentGroupBox->setOutputExtentFromCurrent();

mScaleWidget->setScale( 1 / mapCanvas->mapSettings().scale() );
mScaleWidget->setMapCanvas( mapCanvas );
mScaleWidget->setShowCurrentScaleButton( true );

mDrawDecorations->setText( QString( "Draw active decorations: %1" ).arg( !activeDecorations.isEmpty() ? activeDecorations : tr( "none" ) ) );

connect( mResolutionSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateDpi );
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsMapSaveDialog::updateExtent );
connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsMapSaveDialog::updateScale );

updateOutputSize();
}

void QgsMapSaveDialog::updateDpi( int dpi )
{
mSize *= ( double )dpi / mDpi;
mDpi = dpi;

updateOutputSize();
}

void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
{
mSize.setWidth( mSize.width() * extent.width() / mExtent.width() );
mSize.setHeight( mSize.height() * extent.height() / mExtent.height() );
mExtent = extent;

updateOutputSize();
}

void QgsMapSaveDialog::updateScale( double scale )
{
QgsScaleCalculator calculator;
calculator.setMapUnits( mExtentGroupBox->currentCrs().mapUnits() );
calculator.setDpi( mDpi );

double oldScale = 1 / ( calculator.calculate( mExtent, mSize.width() ) );
double scaleRatio = oldScale / scale;
mExtent.scale( scaleRatio );
mExtentGroupBox->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
}

void QgsMapSaveDialog::updateOutputSize()
{
mOutputSize->setText( QString( "Output size: %1 x %2 pixels" ).arg( mSize.width() ).arg( mSize.height() ) );
}

QgsRectangle QgsMapSaveDialog::extent() const
{
return mExtentGroupBox->outputExtent();
}

int QgsMapSaveDialog::dpi() const
{
return mResolutionSpinBox->value();
}

QSize QgsMapSaveDialog::size() const
{
return mSize;
}

bool QgsMapSaveDialog::drawAnnotations() const
{
return mDrawAnnotations->isChecked();
}

bool QgsMapSaveDialog::drawDecorations() const
{
return mDrawDecorations->isChecked();
}
72 changes: 72 additions & 0 deletions src/app/qgsmapsavedialog.h
@@ -0,0 +1,72 @@
/***************************************************************************
qgsmapsavedialog.h
-------------------------------------
begin : April 2017
copyright : (C) 2017 by Mathieu Pellerin
email : nirvn dot asia at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSMAPSAVEDIALOG_H
#define QGSMAPSAVEDIALOG_H

#include "ui_qgsmapsavedialog.h"

#include "qgisapp.h"
#include "qgsrectangle.h"
#include "qgsmapcanvas.h"

#include <QDialog>
#include <QSize>

/** \ingroup app
* \brief a dialog for saving a map to an image.
* \since QGIS 3.0
*/
class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog
{
Q_OBJECT

public:

/** Constructor for QgsMapSaveDialog
*/
QgsMapSaveDialog( QWidget *parent = nullptr, QgsMapCanvas *mapCanvas = nullptr, const QString &activeDecorations = QString() );

//! returns extent rectangle
QgsRectangle extent() const;

//! returns the numerical value of the dpi spin box
int dpi() const;

//! returns the output size
QSize size() const;

//! returns whether the draw annotations element is checked
bool drawAnnotations() const;

//! returns whether the draw decorations element is checked
bool drawDecorations() const;

private:

void updateDpi( int dpi );
void updateExtent( const QgsRectangle &extent );
void updateScale( double scale );
void updateOutputSize();

QgsRectangle mExtent;
int mDpi;
QSize mSize;

};

#endif // QGSMAPSAVEDIALOG_H
11 changes: 11 additions & 0 deletions src/core/qgsmaprenderertask.cpp
Expand Up @@ -46,6 +46,12 @@ void QgsMapRendererTask::addAnnotations( QList< QgsAnnotation * > annotations )
}
}

void QgsMapRendererTask::addDecorations( QList< QgsMapDecoration * > decorations )
{
mDecorations = decorations;
}


void QgsMapRendererTask::cancel()
{
mJobMutex.lock();
Expand Down Expand Up @@ -97,6 +103,11 @@ bool QgsMapRendererTask::run()
QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
context.setPainter( destPainter );

Q_FOREACH ( QgsMapDecoration *decoration, mDecorations )
{
decoration->render( mMapSettings, context );
}

Q_FOREACH ( QgsAnnotation *annotation, mAnnotations )
{
if ( isCanceled() )
Expand Down
7 changes: 7 additions & 0 deletions src/core/qgsmaprenderertask.h
Expand Up @@ -23,6 +23,7 @@
#include "qgsannotation.h"
#include "qgsannotationmanager.h"
#include "qgsmapsettings.h"
#include "qgsmapdecoration.h"
#include "qgstaskmanager.h"
#include "qgsmaprenderercustompainterjob.h"

Expand Down Expand Up @@ -67,6 +68,11 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
*/
void addAnnotations( QList< QgsAnnotation * > annotations );

/**
* Adds \a decorations to be rendered on the map.
*/
void addDecorations( QList< QgsMapDecoration * > decorations );

void cancel() override;

signals:
Expand Down Expand Up @@ -99,6 +105,7 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
QString mFileFormat;

QList< QgsAnnotation * > mAnnotations;
QList< QgsMapDecoration * > mDecorations;

int mError = 0;
};
Expand Down
4 changes: 4 additions & 0 deletions src/gui/qgsextentgroupbox.cpp
Expand Up @@ -29,6 +29,8 @@ QgsExtentGroupBox::QgsExtentGroupBox( QWidget *parent )
mYMinLineEdit->setValidator( new QDoubleValidator( this ) );
mYMaxLineEdit->setValidator( new QDoubleValidator( this ) );

mOriginalExtentButton->setVisible( false );

connect( mCurrentExtentButton, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromCurrent );
connect( mOriginalExtentButton, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromOriginal );
connect( this, &QGroupBox::clicked, this, &QgsExtentGroupBox::groupBoxClicked );
Expand All @@ -39,6 +41,8 @@ void QgsExtentGroupBox::setOriginalExtent( const QgsRectangle &originalExtent, c
{
mOriginalExtent = originalExtent;
mOriginalCrs = originalCrs;

mOriginalExtentButton->setVisible( true );
}


Expand Down

0 comments on commit 268acab

Please sign in to comment.