Skip to content

Commit 81ccfb0

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

15 files changed

+327
-27
lines changed
 

‎python/core/core.sip

Lines changed: 1 addition & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 43 additions & 0 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 35 additions & 1 deletion
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 20 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 44 additions & 0 deletions
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

Lines changed: 43 additions & 0 deletions
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

Lines changed: 3 additions & 18 deletions
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

‎src/ui/qgsmapsavedialog.ui

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,17 @@
1616
<layout class="QVBoxLayout" name="verticalLayout">
1717
<item>
1818
<layout class="QGridLayout" name="gridLayout">
19-
<item row="5" column="0" colspan="2">>
19+
<item row="7" column="0" colspan="2">
20+
<widget class="QCheckBox" name="mSaveWorldFile">
21+
<property name="text">
22+
<string>Save world file</string>
23+
</property>
24+
<property name="checked">
25+
<bool>true</bool>
26+
</property>
27+
</widget>
28+
</item>
29+
<item row="6" column="0" colspan="2">>
2030
<widget class="QCheckBox" name="mDrawAnnotations">
2131
<property name="text">
2232
<string>Draw annotations</string>
@@ -26,7 +36,7 @@
2636
</property>
2737
</widget>
2838
</item>
29-
<item row="4" column="0" colspan="2">
39+
<item row="5" column="0" colspan="2">
3040
<widget class="QCheckBox" name="mDrawDecorations">
3141
<property name="text">
3242
<string>Draw active decorations</string>
@@ -36,13 +46,55 @@
3646
</property>
3747
</widget>
3848
</item>
39-
<item row="3" column="0" colspan="2">
40-
<widget class="QLabel" name="mOutputSize">
41-
<property name="enabled">
49+
<item row="4" column="0">
50+
<widget class="QLabel" name="label_4">
51+
<property name="text">
52+
<string>Output height</string>
53+
</property>
54+
</widget>
55+
</item>
56+
<item row="4" column="1">
57+
<widget class="QgsSpinBox" name="mOutputHeightSpinBox">
58+
<property name="suffix">
59+
<string> px</string>
60+
</property>
61+
<property name="prefix">
62+
<string/>
63+
</property>
64+
<property name="minimum">
65+
<number>1</number>
66+
</property>
67+
<property name="maximum">
68+
<number>99999</number>
69+
</property>
70+
<property name="showClearButton" stdset="0">
4271
<bool>false</bool>
4372
</property>
44-
<property name="alignment">
45-
<set>Qt::AlignCenter</set>
73+
</widget>
74+
</item>
75+
<item row="3" column="0">
76+
<widget class="QLabel" name="label_3">
77+
<property name="text">
78+
<string>Output width</string>
79+
</property>
80+
</widget>
81+
</item>
82+
<item row="3" column="1">
83+
<widget class="QgsSpinBox" name="mOutputWidthSpinBox">
84+
<property name="suffix">
85+
<string> px</string>
86+
</property>
87+
<property name="prefix">
88+
<string/>
89+
</property>
90+
<property name="minimum">
91+
<number>1</number>
92+
</property>
93+
<property name="maximum">
94+
<number>99999</number>
95+
</property>
96+
<property name="showClearButton" stdset="0">
97+
<bool>false</bool>
4698
</property>
4799
</widget>
48100
</item>
@@ -61,6 +113,9 @@
61113
<property name="prefix">
62114
<string/>
63115
</property>
116+
<property name="minimum">
117+
<number>1</number>
118+
</property>
64119
<property name="maximum">
65120
<number>3000</number>
66121
</property>

‎tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ SET(TESTS
128128
testqgsmaprendererjob.cpp
129129
testqgsmaprotation.cpp
130130
testqgsmapsettings.cpp
131+
testqgsmapsettingsutils.cpp
131132
testqgsmaptopixelgeometrysimplifier.cpp
132133
testqgsmaptopixel.cpp
133134
testqgsmarkerlinesymbol.cpp
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/***************************************************************************
2+
testqgsmapsettingsutils.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 "qgstest.h"
19+
20+
#include "qgsmapsettings.h"
21+
#include "qgsmapsettingsutils.h"
22+
23+
#include <QString>
24+
25+
class TestQgsMapSettingsUtils : public QObject
26+
{
27+
Q_OBJECT
28+
29+
private slots:
30+
void initTestCase();// will be called before the first testfunction is executed.
31+
void cleanupTestCase() {} // will be called after the last testfunction was executed.
32+
void init() {} // will be called before each testfunction is executed.
33+
void cleanup() {} // will be called after each testfunction was executed.
34+
35+
void createWorldFileContent(); //test world file content function
36+
37+
private:
38+
39+
QgsMapSettings mMapSettings;
40+
41+
};
42+
43+
void TestQgsMapSettingsUtils::initTestCase()
44+
{
45+
mMapSettings.setExtent( QgsRectangle( 0, 0, 1, 1 ) );
46+
}
47+
48+
void TestQgsMapSettingsUtils::createWorldFileContent()
49+
{
50+
QCOMPARE( QgsMapSettingsUtils::worldFileContent( mMapSettings ), QString( "1\r\n0\r\n0\r\n-1\r\n0.5\r\n0.5\r\n" ) );
51+
}
52+
53+
QGSTEST_MAIN( TestQgsMapSettingsUtils )
54+
#include "testqgsmapsettingsutils.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.