Skip to content

Commit 387c88d

Browse files
Hugo Merciermhugent
Hugo Mercier
authored andcommitted
Add world file generation option. Work funded by Tuscany Region - SITA. Contract Support to the use of GFOSS \(Geographic Free and Open Source Software\) Desktop tools \(CIG Z3B06FA6D7\)
1 parent f66f9aa commit 387c88d

7 files changed

+276
-2
lines changed

src/app/composer/qgscomposer.cpp

+68
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,22 @@ void QgsComposer::on_mActionExportAsImage_triggered()
995995
image.save( outputFilePath, fileNExt.second.toLocal8Bit().constData() );
996996
}
997997
}
998+
999+
//
1000+
// Write the world file if asked to
1001+
if ( mComposition->generateWorldFile() )
1002+
{
1003+
double params[6];
1004+
mComposition->computeWorldFileParameters( params );
1005+
1006+
QFileInfo fi( fileNExt.first );
1007+
// build the world file name
1008+
QString worldFileName = fi.absolutePath() + "/" + fi.baseName() + "."
1009+
+ fi.suffix()[0] + fi.suffix()[fi.suffix().size()-1] + "w";
1010+
1011+
writeWorldFile( worldFileName, params );
1012+
}
1013+
9981014
mView->setPaintingEnabled( true );
9991015
}
10001016
else
@@ -1137,6 +1153,21 @@ void QgsComposer::on_mActionExportAsImage_triggered()
11371153
image.save( outputFilePath, format.toLocal8Bit().constData() );
11381154
}
11391155
}
1156+
1157+
//
1158+
// Write the world file if asked to
1159+
if ( mComposition->generateWorldFile() )
1160+
{
1161+
double params[6];
1162+
mComposition->computeWorldFileParameters( params );
1163+
1164+
QFileInfo fi( filename );
1165+
// build the world file name
1166+
QString worldFileName = fi.absolutePath() + "/" + fi.baseName() + "."
1167+
+ fi.suffix()[0] + fi.suffix()[fi.suffix().size()-1] + "w";
1168+
1169+
writeWorldFile( worldFileName, params );
1170+
}
11401171
}
11411172
atlasMap->endRender();
11421173
mView->setPaintingEnabled( true );
@@ -1881,6 +1912,24 @@ void QgsComposer::readXML( const QDomElement& composerElem, const QDomDocument&
18811912
mComposition->addItemsFromXML( composerElem, doc, &mMapsToRestore );
18821913
}
18831914

1915+
// look for world file composer map, if needed
1916+
// Note: this must be done after maps have been added by addItemsFromXML
1917+
if ( mComposition->generateWorldFile() )
1918+
{
1919+
QDomElement compositionElem = compositionNodeList.at( 0 ).toElement();
1920+
QgsComposerMap* worldFileMap = 0;
1921+
QList<const QgsComposerMap*> maps = mComposition->composerMapItems();
1922+
for ( QList<const QgsComposerMap*>::const_iterator it = maps.begin(); it != maps.end(); ++it )
1923+
{
1924+
if (( *it )->id() == compositionElem.attribute( "worldFileMap" ).toInt() )
1925+
{
1926+
worldFileMap = const_cast<QgsComposerMap*>( *it );
1927+
break;
1928+
}
1929+
}
1930+
mComposition->setWorldFileMap( worldFileMap );
1931+
}
1932+
18841933
mComposition->sortZList();
18851934
mView->setComposition( mComposition );
18861935

@@ -2304,3 +2353,22 @@ void QgsComposer::createComposerView()
23042353
mView->setVerticalRuler( mVerticalRuler );
23052354
mViewLayout->addWidget( mView, 1, 1 );
23062355
}
2356+
2357+
void QgsComposer::writeWorldFile( QString worldFileName, double p[6] ) const
2358+
{
2359+
QFile worldFile( worldFileName );
2360+
if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
2361+
{
2362+
return;
2363+
}
2364+
QTextStream fout( &worldFile );
2365+
2366+
// QString::number does not use locale settings (for the decimal point)
2367+
// which is what we want here
2368+
fout << QString::number( p[0] ) << "\r\n";
2369+
fout << QString::number( p[3] ) << "\r\n";
2370+
fout << QString::number( p[1] ) << "\r\n";
2371+
fout << QString::number( p[4] ) << "\r\n";
2372+
fout << QString::number( p[2] ) << "\r\n";
2373+
fout << QString::number( p[5] ) << "\r\n";
2374+
}

src/app/composer/qgscomposer.h

+3
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
334334
//! Create composer view and rulers
335335
void createComposerView();
336336

337+
//! Write a world file
338+
void writeWorldFile( QString fileName, double params[6] ) const;
339+
337340
/**Composer title*/
338341
QString mTitle;
339342

src/app/composer/qgscompositionwidget.cpp

+89
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <qgis.h>
1818
#include "qgscompositionwidget.h"
1919
#include "qgscomposition.h"
20+
#include "qgscomposermap.h"
21+
#include "qgscomposeritem.h"
2022
#include <QColorDialog>
2123
#include <QWidget>
2224
#include <QPrinter> //for screen resolution
@@ -49,6 +51,29 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c )
4951
//print as raster
5052
mPrintAsRasterCheckBox->setChecked( mComposition->printAsRaster() );
5153

54+
// world file generation
55+
mGenerateWorldFileCheckBox->setChecked( mComposition->generateWorldFile() );
56+
57+
// populate the map list
58+
mWorldFileMapComboBox->clear();
59+
QList<const QgsComposerMap*> availableMaps = mComposition->composerMapItems();
60+
QList<const QgsComposerMap*>::const_iterator mapItemIt = availableMaps.constBegin();
61+
for ( ; mapItemIt != availableMaps.constEnd(); ++mapItemIt )
62+
{
63+
mWorldFileMapComboBox->addItem( tr( "Map %1" ).arg(( *mapItemIt )->id() ), qVariantFromValue(( void* )*mapItemIt ) );
64+
}
65+
66+
int idx = mWorldFileMapComboBox->findData( qVariantFromValue(( void* )mComposition->worldFileMap() ) );
67+
if ( idx != -1 )
68+
{
69+
mWorldFileMapComboBox->setCurrentIndex( idx );
70+
}
71+
72+
// Connect to addition / removal of maps
73+
connect( mComposition, SIGNAL( composerMapAdded( QgsComposerMap* ) ), this, SLOT( onComposerMapAdded( QgsComposerMap* ) ) );
74+
connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( onItemRemoved( QgsComposerItem* ) ) );
75+
76+
5277
mAlignmentSnapGroupCheckBox->setChecked( mComposition->alignmentSnap() );
5378
mAlignmentToleranceSpinBox->setValue( mComposition->alignmentSnapTolerance() );
5479

@@ -412,6 +437,70 @@ void QgsCompositionWidget::on_mPrintAsRasterCheckBox_toggled( bool state )
412437
mComposition->setPrintAsRaster( state );
413438
}
414439

440+
void QgsCompositionWidget::on_mGenerateWorldFileCheckBox_toggled( bool state )
441+
{
442+
if ( !mComposition )
443+
{
444+
return;
445+
}
446+
447+
mComposition->setGenerateWorldFile( state );
448+
mWorldFileMapComboBox->setEnabled( state );
449+
}
450+
451+
void QgsCompositionWidget::onComposerMapAdded( QgsComposerMap* map )
452+
{
453+
if ( !mComposition )
454+
{
455+
return;
456+
}
457+
458+
mWorldFileMapComboBox->addItem( tr( "Map %1" ).arg( map->id() ), qVariantFromValue(( void* )map ) );
459+
if ( mWorldFileMapComboBox->count() == 1 )
460+
{
461+
mComposition->setWorldFileMap( map );
462+
}
463+
}
464+
465+
void QgsCompositionWidget::onItemRemoved( QgsComposerItem* item )
466+
{
467+
if ( !mComposition )
468+
{
469+
return;
470+
}
471+
472+
QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
473+
if ( map )
474+
{
475+
int idx = mWorldFileMapComboBox->findData( qVariantFromValue(( void* )map ) );
476+
if ( idx != -1 )
477+
{
478+
mWorldFileMapComboBox->removeItem( idx );
479+
}
480+
}
481+
if ( mWorldFileMapComboBox->count() == 0 )
482+
{
483+
mComposition->setWorldFileMap( 0 );
484+
}
485+
}
486+
487+
void QgsCompositionWidget::on_mWorldFileMapComboBox_currentIndexChanged( int index )
488+
{
489+
if ( !mComposition )
490+
{
491+
return;
492+
}
493+
if ( index == -1 )
494+
{
495+
mComposition->setWorldFileMap( 0 );
496+
}
497+
else
498+
{
499+
QgsComposerMap* map = reinterpret_cast<QgsComposerMap*>( mWorldFileMapComboBox->itemData( index ).value<void*>() );
500+
mComposition->setWorldFileMap( map );
501+
}
502+
}
503+
415504
void QgsCompositionWidget::on_mSnapToGridGroupCheckBox_toggled( bool state )
416505
{
417506
if ( mComposition )

src/app/composer/qgscompositionwidget.h

+10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "ui_qgscompositionwidgetbase.h"
1818

1919
class QgsComposition;
20+
class QgsComposerMap;
21+
class QgsComposerItem;
2022

2123
/** \ingroup MapComposer
2224
* Struct to hold map composer paper properties.
@@ -48,6 +50,8 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase
4850
void on_mNumPagesSpinBox_valueChanged( int value );
4951
void on_mResolutionSpinBox_valueChanged( const int value );
5052
void on_mPrintAsRasterCheckBox_toggled( bool state );
53+
void on_mGenerateWorldFileCheckBox_toggled( bool state );
54+
void on_mWorldFileMapComboBox_currentIndexChanged( int index );
5155

5256
void on_mSnapToGridGroupCheckBox_toggled( bool state );
5357
void on_mGridResolutionSpinBox_valueChanged( double d );
@@ -65,6 +69,12 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase
6569
/**Sets Print as raster checkbox value*/
6670
void setPrintAsRasterCheckBox( bool state );
6771

72+
private slots:
73+
/* when a new map is added */
74+
void onComposerMapAdded( QgsComposerMap* );
75+
/* when a map is deleted */
76+
void onItemRemoved( QgsComposerItem* );
77+
6878
private:
6979
QgsComposition* mComposition;
7080
QMap<QString, QgsCompositionPaper> mPaperMap;

src/core/composer/qgscomposition.cpp

+73
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
5454
, mPageHeight( 210 )
5555
, mSpaceBetweenPages( 10 )
5656
, mPrintAsRaster( false )
57+
, mGenerateWorldFile( false )
58+
, mWorldFileMap( 0 )
5759
, mUseAdvancedEffects( true )
5860
, mSelectionTolerance( 0.0 )
5961
, mSnapToGrid( false )
@@ -81,6 +83,8 @@ QgsComposition::QgsComposition()
8183
mPageHeight( 210 ),
8284
mSpaceBetweenPages( 10 ),
8385
mPrintAsRaster( false ),
86+
mGenerateWorldFile( false ),
87+
mWorldFileMap( 0 ),
8488
mUseAdvancedEffects( true ),
8589
mSelectionTolerance( 0.0 ),
8690
mSnapToGrid( false ),
@@ -420,6 +424,12 @@ bool QgsComposition::writeXML( QDomElement& composerElem, QDomDocument& doc )
420424
compositionElem.setAttribute( "printResolution", mPrintResolution );
421425
compositionElem.setAttribute( "printAsRaster", mPrintAsRaster );
422426

427+
compositionElem.setAttribute( "generateWorldFile", mGenerateWorldFile ? 1 : 0 );
428+
if ( mGenerateWorldFile && mWorldFileMap )
429+
{
430+
compositionElem.setAttribute( "worldFileMap", mWorldFileMap->id() );
431+
}
432+
423433
compositionElem.setAttribute( "alignmentSnap", mAlignmentSnap ? 1 : 0 );
424434
compositionElem.setAttribute( "alignmentSnapTolerance", mAlignmentSnapTolerance );
425435

@@ -505,6 +515,8 @@ bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocu
505515
mPrintAsRaster = compositionElem.attribute( "printAsRaster" ).toInt();
506516
mPrintResolution = compositionElem.attribute( "printResolution", "300" ).toInt();
507517

518+
mGenerateWorldFile = compositionElem.attribute( "generateWorldFile", "0" ).toInt() == 1 ? true : false;
519+
508520
updatePaperItems();
509521

510522
return true;
@@ -2174,3 +2186,64 @@ bool QgsComposition::nearestItem( const QMap< double, const QgsComposerItem* >&
21742186
}
21752187
}
21762188

2189+
void QgsComposition::computeWorldFileParameters( double p[6] ) const
2190+
{
2191+
//
2192+
// Word file parameters : affine transformation parameters from pixel coordinates to map coordinates
2193+
2194+
if ( !mWorldFileMap )
2195+
{
2196+
return;
2197+
}
2198+
2199+
QRectF brect = mWorldFileMap->boundingRect();
2200+
QgsRectangle extent = mWorldFileMap->extent();
2201+
2202+
double alpha = mWorldFileMap->rotation() / 180 * M_PI;
2203+
2204+
double xr = extent.width() / brect.width();
2205+
double yr = extent.height() / brect.height();
2206+
2207+
double XC = extent.center().x();
2208+
double YC = extent.center().y();
2209+
2210+
// get the extent for the page
2211+
double xmin = extent.xMinimum() - mWorldFileMap->transform().dx() * xr;
2212+
double ymax = extent.yMaximum() + mWorldFileMap->transform().dy() * yr;
2213+
QgsRectangle paperExtent( xmin, ymax - paperHeight() * yr, xmin + paperWidth() * xr, ymax );
2214+
2215+
double X0 = paperExtent.xMinimum();
2216+
double Y0 = paperExtent.yMinimum();
2217+
2218+
int widthPx = ( int )( printResolution() * paperWidth() / 25.4 );
2219+
int heightPx = ( int )( printResolution() * paperHeight() / 25.4 );
2220+
2221+
double Ww = paperExtent.width() / widthPx;
2222+
double Hh = paperExtent.height() / heightPx;
2223+
2224+
// scaling matrix
2225+
double s[6];
2226+
s[0] = Ww;
2227+
s[1] = 0;
2228+
s[2] = X0;
2229+
s[3] = 0;
2230+
s[4] = -Hh;
2231+
s[5] = Y0 + paperExtent.height();
2232+
2233+
// rotation matrix
2234+
double r[6];
2235+
r[0] = cos( alpha );
2236+
r[1] = -sin( alpha );
2237+
r[2] = XC * ( 1 - cos( alpha ) ) + YC * sin( alpha );
2238+
r[3] = sin( alpha );
2239+
r[4] = cos( alpha );
2240+
r[5] = - XC * sin( alpha ) + YC * ( 1 - cos( alpha ) );
2241+
2242+
// result = rotation x scaling = rotation(scaling(X))
2243+
p[0] = r[0] * s[0] + r[1] * s[3];
2244+
p[1] = r[0] * s[1] + r[1] * s[4];
2245+
p[2] = r[0] * s[2] + r[1] * s[5] + r[2];
2246+
p[3] = r[3] * s[0] + r[4] * s[3];
2247+
p[4] = r[3] * s[1] + r[4] * s[4];
2248+
p[5] = r[3] * s[2] + r[4] * s[5] + r[5];
2249+
}

src/core/composer/qgscomposition.h

+14
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
182182
bool printAsRaster() const {return mPrintAsRaster;}
183183
void setPrintAsRaster( bool enabled ) { mPrintAsRaster = enabled; }
184184

185+
bool generateWorldFile() const { return mGenerateWorldFile; }
186+
void setGenerateWorldFile( bool enabled ) { mGenerateWorldFile = enabled; }
187+
188+
QgsComposerMap* worldFileMap() const { return mWorldFileMap; }
189+
void setWorldFileMap( QgsComposerMap* map ) { mWorldFileMap = map; }
190+
185191
/**Returns true if a composition should use advanced effects such as blend modes
186192
@note added in 1.9*/
187193
bool useAdvancedEffects() const {return mUseAdvancedEffects;}
@@ -357,6 +363,9 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
357363
@note added in version 1.9*/
358364
void renderPage( QPainter* p, int page );
359365

366+
/** Compute world file parameters */
367+
void computeWorldFileParameters( double p[6] ) const;
368+
360369
QgsAtlasComposition& atlasComposition() { return mAtlasComposition; }
361370

362371
public slots:
@@ -384,6 +393,11 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
384393
/**Flag if map should be printed as a raster (via QImage). False by default*/
385394
bool mPrintAsRaster;
386395

396+
/**Flag if a world file should be generated on raster export */
397+
bool mGenerateWorldFile;
398+
/** Composer map to use for the world file generation */
399+
QgsComposerMap* mWorldFileMap;
400+
387401
/**Flag if advanced visual effects such as blend modes should be used. True by default*/
388402
bool mUseAdvancedEffects;
389403

0 commit comments

Comments
 (0)