Skip to content

Commit 81ccfb0

Browse files
authored
Merge pull request #4460 from nirvn/wallpapers
2 parents 7d45914 + 295c212 commit 81ccfb0

15 files changed

+327
-27
lines changed

python/core/core.sip

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
%Include qgsmaprenderersequentialjob.sip
101101
%Include qgsmaprenderertask.sip
102102
%Include qgsmapsettings.sip
103+
%Include qgsmapsettingsutils.sip
103104
%Include qgsmaptopixel.sip
104105
%Include qgsmapunitscale.sip
105106
%Include qgsmargins.sip

python/core/qgsmaprenderertask.sip

+5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ class QgsMapRendererTask : QgsTask
5252
Adds ``decorations`` to be rendered on the map.
5353
%End
5454

55+
void setSaveWorldFile( bool save );
56+
%Docstring
57+
Sets whether a world file will be created alongside an image file.
58+
%End
59+
5560
virtual void cancel();
5661

5762

python/core/qgsmapsettingsutils.sip

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsmapsettingsutils.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
class QgsMapSettingsUtils
14+
{
15+
%Docstring
16+
Utilities for map settings.
17+
.. versionadded:: 3.0
18+
%End
19+
20+
%TypeHeaderCode
21+
#include "qgsmapsettingsutils.h"
22+
%End
23+
public:
24+
25+
static QString worldFileContent( const QgsMapSettings &mapSettings );
26+
%Docstring
27+
Creates the content of a world file.
28+
\param mapSettings map settings
29+
.. note::
30+
31+
Uses 17 places of precision for all numbers output
32+
:rtype: str
33+
%End
34+
35+
};
36+
37+
/************************************************************************
38+
* This file has been generated automatically from *
39+
* *
40+
* src/core/qgsmapsettingsutils.h *
41+
* *
42+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
43+
************************************************************************/

src/app/qgisapp.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -5833,9 +5833,11 @@ void QgisApp::saveMapAsImage()
58335833
mapRendererTask->addDecorations( decorations );
58345834
}
58355835

5836+
mapRendererTask->setSaveWorldFile( dlg.saveWorldFile() );
5837+
58365838
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ = ]
58375839
{
5838-
messageBar()->pushSuccess( tr( "Save as image" ), tr( "Successfully saved canvas to image" ) );
5840+
messageBar()->pushSuccess( tr( "Save as image" ), tr( "Successfully saved map to image" ) );
58395841
} );
58405842
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, this, [ = ]( int error )
58415843
{

src/app/qgsmapsavedialog.cpp

+35-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, co
5050
mDrawDecorations->setText( tr( "Draw active decorations: %1" ).arg( !activeDecorations.isEmpty() ? activeDecorations : tr( "none" ) ) );
5151

5252
connect( mResolutionSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateDpi );
53+
connect( mOutputWidthSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateOutputWidth );
54+
connect( mOutputHeightSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateOutputHeight );
5355
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsMapSaveDialog::updateExtent );
5456
connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsMapSaveDialog::updateScale );
5557

@@ -64,6 +66,32 @@ void QgsMapSaveDialog::updateDpi( int dpi )
6466
updateOutputSize();
6567
}
6668

69+
void QgsMapSaveDialog::updateOutputWidth( int width )
70+
{
71+
double scale = ( double )width / mSize.width();
72+
double adjustment = ( ( mExtent.width() * scale ) - mExtent.width() ) / 2;
73+
74+
mExtent.setXMinimum( mExtent.xMinimum() - adjustment );
75+
mExtent.setXMaximum( mExtent.xMaximum() + adjustment );
76+
77+
whileBlocking( mExtentGroupBox )->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
78+
79+
mSize.setWidth( width );
80+
}
81+
82+
void QgsMapSaveDialog::updateOutputHeight( int height )
83+
{
84+
double scale = ( double )height / mSize.height();
85+
double adjustment = ( ( mExtent.height() * scale ) - mExtent.height() ) / 2;
86+
87+
mExtent.setYMinimum( mExtent.yMinimum() - adjustment );
88+
mExtent.setYMaximum( mExtent.yMaximum() + adjustment );
89+
90+
whileBlocking( mExtentGroupBox )->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
91+
92+
mSize.setHeight( height );
93+
}
94+
6795
void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
6896
{
6997
mSize.setWidth( mSize.width() * extent.width() / mExtent.width() );
@@ -87,7 +115,8 @@ void QgsMapSaveDialog::updateScale( double scale )
87115

88116
void QgsMapSaveDialog::updateOutputSize()
89117
{
90-
mOutputSize->setText( tr( "Output size: %1 x %2 pixels" ).arg( mSize.width() ).arg( mSize.height() ) );
118+
whileBlocking( mOutputWidthSpinBox )->setValue( mSize.width() );
119+
whileBlocking( mOutputHeightSpinBox )->setValue( mSize.height() );
91120
}
92121

93122
QgsRectangle QgsMapSaveDialog::extent() const
@@ -114,3 +143,8 @@ bool QgsMapSaveDialog::drawDecorations() const
114143
{
115144
return mDrawDecorations->isChecked();
116145
}
146+
147+
bool QgsMapSaveDialog::saveWorldFile() const
148+
{
149+
return mSaveWorldFile->isChecked();
150+
}

src/app/qgsmapsavedialog.h

+5
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,14 @@ class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog
5656
//! returns whether the draw decorations element is checked
5757
bool drawDecorations() const;
5858

59+
//! returns whether a world file will be created
60+
bool saveWorldFile() const;
61+
5962
private:
6063

6164
void updateDpi( int dpi );
65+
void updateOutputWidth( int width );
66+
void updateOutputHeight( int height );
6267
void updateExtent( const QgsRectangle &extent );
6368
void updateScale( double scale );
6469
void updateOutputSize();

src/core/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ SET(QGIS_CORE_SRCS
184184
qgsmaprenderersequentialjob.cpp
185185
qgsmaprenderertask.cpp
186186
qgsmapsettings.cpp
187+
qgsmapsettingsutils.cpp
187188
qgsmaptopixel.cpp
188189
qgsmaptopixelgeometrysimplifier.cpp
189190
qgsmapunitscale.cpp
@@ -751,6 +752,7 @@ SET(QGIS_CORE_HDRS
751752
qgsmaplayerrenderer.h
752753
qgsmaplayerstylemanager.h
753754
qgsmapsettings.h
755+
qgsmapsettingsutils.h
754756
qgsmaptopixel.h
755757
qgsmaptopixelgeometrysimplifier.h
756758
qgsmapunitscale.h

src/core/qgsmaprenderertask.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
#include "qgsannotation.h"
1919
#include "qgsannotationmanager.h"
2020
#include "qgsmaprenderertask.h"
21+
#include "qgsmapsettingsutils.h"
2122

23+
#include <QFile>
24+
#include <QTextStream>
2225

2326
QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat )
2427
: QgsTask( tr( "Saving as image" ) )
@@ -154,6 +157,23 @@ bool QgsMapRendererTask::run()
154157
mError = ImageSaveFail;
155158
return false;
156159
}
160+
161+
if ( mSaveWorldFile )
162+
{
163+
QFileInfo info = QFileInfo( mFileName );
164+
165+
// build the world file name
166+
QString outputSuffix = info.suffix();
167+
QString worldFileName = info.absolutePath() + '/' + info.baseName() + '.'
168+
+ outputSuffix.at( 0 ) + outputSuffix.at( info.suffix().size() - 1 ) + 'w';
169+
QFile worldFile( worldFileName );
170+
171+
if ( worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
172+
{
173+
QTextStream stream( &worldFile );
174+
stream << QgsMapSettingsUtils::worldFileContent( mMapSettings );
175+
}
176+
}
157177
}
158178

159179
return true;

src/core/qgsmaprenderertask.h

+6
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
7373
*/
7474
void addDecorations( QList< QgsMapDecoration * > decorations );
7575

76+
/**
77+
* Sets whether a world file will be created alongside an image file.
78+
*/
79+
void setSaveWorldFile( bool save ) { mSaveWorldFile = save; }
80+
7681
void cancel() override;
7782

7883
signals:
@@ -103,6 +108,7 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
103108

104109
QString mFileName;
105110
QString mFileFormat;
111+
bool mSaveWorldFile = false;
106112

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

src/core/qgsmapsettingsutils.cpp

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/***************************************************************************
2+
qgsmapsettingsutils.cpp
3+
-------------------
4+
begin : May 2017
5+
copyright : (C) 2017 by Mathieu Pellerin
6+
email : nirvn dot asia at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsmapsettings.h"
19+
#include "qgsmapsettingsutils.h"
20+
21+
#include <QString>
22+
23+
QString QgsMapSettingsUtils::worldFileContent( const QgsMapSettings &mapSettings )
24+
{
25+
double xOrigin = mapSettings.visiblePolygon().at( 0 ).x() + ( mapSettings.mapUnitsPerPixel() / 2 );
26+
double yOrigin = mapSettings.visiblePolygon().at( 0 ).y() - ( mapSettings.mapUnitsPerPixel() / 2 );
27+
28+
QString content;
29+
// Pixel XDim
30+
content += qgsDoubleToString( mapSettings.mapUnitsPerPixel() ) + "\r\n";
31+
// Rotation on y axis
32+
content += QString( "%1\r\n" ).arg( mapSettings.rotation() );
33+
// Rotation on x axis
34+
content += QString( "%1\r\n" ).arg( mapSettings.rotation() );
35+
// Pixel YDim - almost always negative
36+
// See https://en.wikipedia.org/wiki/World_file#cite_ref-3
37+
content += '-' + qgsDoubleToString( mapSettings.mapUnitsPerPixel() ) + "\r\n";
38+
// Origin X (center of top left cell)
39+
content += qgsDoubleToString( xOrigin ) + "\r\n";
40+
// Origin Y (center of top left cell)
41+
content += qgsDoubleToString( yOrigin ) + "\r\n";
42+
43+
return content;
44+
}

src/core/qgsmapsettingsutils.h

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/***************************************************************************
2+
qgsmapsettingsutils.h
3+
-------------------
4+
begin : May 2017
5+
copyright : (C) 2017 by Mathieu Pellerin
6+
email : nirvn dot asia at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSMAPSETTINGSUTILS_H
19+
#define QGSMAPSETTINGSUTILS_H
20+
21+
#include "qgis_core.h"
22+
#include "qgsmapsettings.h"
23+
24+
#include <QString>
25+
26+
/** \ingroup core
27+
* Utilities for map settings.
28+
* \since QGIS 3.0
29+
*/
30+
class CORE_EXPORT QgsMapSettingsUtils
31+
{
32+
33+
public:
34+
35+
/** Creates the content of a world file.
36+
* \param mapSettings map settings
37+
* \note Uses 17 places of precision for all numbers output
38+
*/
39+
static QString worldFileContent( const QgsMapSettings &mapSettings );
40+
41+
};
42+
43+
#endif

src/gui/qgsmapcanvas.cpp

+3-18
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ email : sherman at mrcc.com
5656
#include "qgsmaprenderercustompainterjob.h"
5757
#include "qgsmaprendererparalleljob.h"
5858
#include "qgsmaprenderersequentialjob.h"
59+
#include "qgsmapsettingsutils.h"
5960
#include "qgsmessagelog.h"
6061
#include "qgsmessageviewer.h"
6162
#include "qgspallabeling.h"
@@ -723,24 +724,8 @@ void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, co
723724
painter.end();
724725
image.save( fileName, format.toLocal8Bit().data() );
725726

726-
//create a world file to go with the image...
727-
QgsRectangle myRect = mapSettings().visibleExtent();
728-
QString myHeader;
729-
// note: use 17 places of precision for all numbers output
730-
//Pixel XDim
731-
myHeader += qgsDoubleToString( mapUnitsPerPixel() ) + "\r\n";
732-
//Rotation on y axis - hard coded
733-
myHeader += QLatin1String( "0 \r\n" );
734-
//Rotation on x axis - hard coded
735-
myHeader += QLatin1String( "0 \r\n" );
736-
//Pixel YDim - almost always negative - see
737-
//http://en.wikipedia.org/wiki/World_file#cite_note-2
738-
myHeader += '-' + qgsDoubleToString( mapUnitsPerPixel() ) + "\r\n";
739-
//Origin X (center of top left cell)
740-
myHeader += qgsDoubleToString( myRect.xMinimum() + ( mapUnitsPerPixel() / 2 ) ) + "\r\n";
741-
//Origin Y (center of top left cell)
742-
myHeader += qgsDoubleToString( myRect.yMaximum() - ( mapUnitsPerPixel() / 2 ) ) + "\r\n";
743727
QFileInfo myInfo = QFileInfo( fileName );
728+
744729
// build the world file name
745730
QString outputSuffix = myInfo.suffix();
746731
QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.baseName() + '.'
@@ -751,7 +736,7 @@ void QgsMapCanvas::saveAsImage( const QString &fileName, QPixmap *theQPixmap, co
751736
return;
752737
}
753738
QTextStream myStream( &myWorldFile );
754-
myStream << myHeader;
739+
myStream << QgsMapSettingsUtils::worldFileContent( mapSettings() );
755740
} // saveAsImage
756741

757742

0 commit comments

Comments
 (0)