Skip to content

Commit

Permalink
Port world file generation code to layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 17, 2017
1 parent afbd140 commit 56383e4
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 17 deletions.
16 changes: 16 additions & 0 deletions python/core/layout/qgslayoutexporter.sip
Expand Up @@ -174,6 +174,22 @@ Resolution to export layout at
:rtype: bool
%End

void computeWorldFileParameters( double &a, double &b, double &c, double &d, double &e, double &f, double dpi = -1 ) const;
%Docstring
Compute world file parameters. Assumes the whole page containing the reference map item
will be exported.

The ``dpi`` argument can be set to the actual DPI of exported file, or left as -1 to use the layout's default DPI.
%End

void computeWorldFileParameters( const QRectF &region, double &a, double &b, double &c, double &d, double &e, double &f, double dpi = -1 ) const;
%Docstring
Computes the world file parameters for a specified ``region`` of the layout.

The ``dpi`` argument can be set to the actual DPI of exported file, or left as -1 to use the layout's default DPI.
%End


};


Expand Down
92 changes: 90 additions & 2 deletions src/core/layout/qgslayoutexporter.cpp
Expand Up @@ -212,9 +212,9 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToImage( const QString

if ( page == worldFilePageNo )
{
#if 0
georeferenceOutput( outputFilePath, nullptr, bounds, imageDlg.resolution() );
georeferenceOutput( outputFilePath, nullptr, bounds, settings.dpi );

#if 0
if ( settings.generateWorldFile )
{
// should generate world file for this page
Expand Down Expand Up @@ -356,6 +356,94 @@ bool QgsLayoutExporter::georeferenceOutput( const QString &file, QgsLayoutItemMa
return true;
}

void QgsLayoutExporter::computeWorldFileParameters( double &a, double &b, double &c, double &d, double &e, double &f, double dpi ) const
{
QgsLayoutItemMap *map = mLayout->referenceMap();
if ( !map )
{
return;
}

int pageNumber = map->page();
QgsLayoutItemPage *page = mLayout->pageCollection()->page( pageNumber );
double pageY = page->pos().y();
QSizeF pageSize = page->rect().size();
QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
computeWorldFileParameters( pageRect, a, b, c, d, e, f, dpi );
}

void QgsLayoutExporter::computeWorldFileParameters( const QRectF &exportRegion, double &a, double &b, double &c, double &d, double &e, double &f, double dpi ) const
{
// World file parameters : affine transformation parameters from pixel coordinates to map coordinates
QgsLayoutItemMap *map = mLayout->referenceMap();
if ( !map )
{
return;
}

double destinationHeight = exportRegion.height();
double destinationWidth = exportRegion.width();

QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
QgsRectangle mapExtent = map->extent();

double alpha = map->mapRotation() / 180 * M_PI;

double xRatio = mapExtent.width() / mapItemSceneRect.width();
double yRatio = mapExtent.height() / mapItemSceneRect.height();

double xCenter = mapExtent.center().x();
double yCenter = mapExtent.center().y();

// get the extent (in map units) for the region
QPointF mapItemPos = map->pos();
//adjust item position so it is relative to export region
mapItemPos.rx() -= exportRegion.left();
mapItemPos.ry() -= exportRegion.top();

double xmin = mapExtent.xMinimum() - mapItemPos.x() * xRatio;
double ymax = mapExtent.yMaximum() + mapItemPos.y() * yRatio;
QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );

double X0 = paperExtent.xMinimum();
double Y0 = paperExtent.yMinimum();

if ( dpi < 0 )
dpi = mLayout->context().dpi();

int widthPx = static_cast< int >( dpi * destinationWidth / 25.4 );
int heightPx = static_cast< int >( dpi * destinationHeight / 25.4 );

double Ww = paperExtent.width() / widthPx;
double Hh = paperExtent.height() / heightPx;

// scaling matrix
double s[6];
s[0] = Ww;
s[1] = 0;
s[2] = X0;
s[3] = 0;
s[4] = -Hh;
s[5] = Y0 + paperExtent.height();

// rotation matrix
double r[6];
r[0] = std::cos( alpha );
r[1] = -std::sin( alpha );
r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
r[3] = std::sin( alpha );
r[4] = std::cos( alpha );
r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );

// result = rotation x scaling = rotation(scaling(X))
a = r[0] * s[0] + r[1] * s[3];
b = r[0] * s[1] + r[1] * s[4];
c = r[0] * s[2] + r[1] * s[5] + r[2];
d = r[3] * s[0] + r[4] * s[3];
e = r[3] * s[1] + r[4] * s[4];
f = r[3] * s[2] + r[4] * s[5] + r[5];
}

QImage QgsLayoutExporter::createImage( const QgsLayoutExporter::ImageExportSettings &settings, int page, QRectF &bounds, bool &skipPage ) const
{
bounds = QRectF();
Expand Down
16 changes: 16 additions & 0 deletions src/core/layout/qgslayoutexporter.h
Expand Up @@ -186,6 +186,22 @@ class CORE_EXPORT QgsLayoutExporter
bool georeferenceOutput( const QString &file, QgsLayoutItemMap *referenceMap = nullptr,
const QRectF &exportRegion = QRectF(), double dpi = -1 ) const;

/**
* Compute world file parameters. Assumes the whole page containing the reference map item
* will be exported.
*
* The \a dpi argument can be set to the actual DPI of exported file, or left as -1 to use the layout's default DPI.
*/
void computeWorldFileParameters( double &a, double &b, double &c, double &d, double &e, double &f, double dpi = -1 ) const;

/**
* Computes the world file parameters for a specified \a region of the layout.
*
* The \a dpi argument can be set to the actual DPI of exported file, or left as -1 to use the layout's default DPI.
*/
void computeWorldFileParameters( const QRectF &region, double &a, double &b, double &c, double &d, double &e, double &f, double dpi = -1 ) const;


private:

QPointer< QgsLayout > mLayout;
Expand Down
41 changes: 26 additions & 15 deletions tests/src/core/testqgslayoutmap.cpp
Expand Up @@ -27,6 +27,7 @@
#include "qgsproject.h"
#include "qgsmapthemecollection.h"
#include "qgsproperty.h"
#include "qgslayoutpagecollection.h"
#include <QObject>
#include "qgstest.h"

Expand All @@ -46,8 +47,9 @@ class TestQgsLayoutMap : public QObject
void render();
#if 0
void uniqueId(); //test if map id is adapted when doing copy paste
void worldFileGeneration(); // test world file generation
#endif
void worldFileGeneration(); // test world file generation

void mapPolygonVertices(); // test mapPolygon function with no map rotation
void dataDefinedLayers(); //test data defined layer string
void dataDefinedStyles(); //test data defined styles
Expand Down Expand Up @@ -189,17 +191,31 @@ void TestQgsLayoutMap::uniqueId()

QVERIFY( oldId != newId );
}
#endif

void TestQgsLayoutMap::worldFileGeneration()
{
mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) );
mComposerMap->setMapRotation( 30.0 );
QgsLayout l( QgsProject::instance() );
l.initializeDefaults();
QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l );
page2->setPageSize( "A4", QgsLayoutItemPage::Landscape );
l.pageCollection()->addPage( page2 );

QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
map->setFrameEnabled( true );
map->setLayers( QList<QgsMapLayer *>() << mRasterLayer );
l.addLayoutItem( map );

map->setExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) );
map->setMapRotation( 30.0 );

mComposition->setGenerateWorldFile( true );
mComposition->setReferenceMap( mComposerMap );
l.setReferenceMap( map );

QgsLayoutExporter exporter( &l );

double a, b, c, d, e, f;
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
exporter.computeWorldFileParameters( a, b, c, d, e, f );

QGSCOMPARENEAR( a, 4.18048, 0.001 );
QGSCOMPARENEAR( b, 2.41331, 0.001 );
Expand All @@ -209,8 +225,8 @@ void TestQgsLayoutMap::worldFileGeneration()
QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 );

//test with map on second page. Parameters should be the same
mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 );
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
map->attemptMove( QgsLayoutPoint( 20, 20 ), true, false, 1 );
exporter.computeWorldFileParameters( a, b, c, d, e, f );

QGSCOMPARENEAR( a, 4.18048, 0.001 );
QGSCOMPARENEAR( b, 2.41331, 0.001 );
Expand All @@ -220,21 +236,16 @@ void TestQgsLayoutMap::worldFileGeneration()
QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 );

//test computing parameters for specific region
mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 );
mComposition->computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f );
map->attemptMove( QgsLayoutPoint( 20, 20 ), true, false, 1 );
exporter.computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f );

QGSCOMPARENEAR( a, 4.18061, 0.001 );
QGSCOMPARENEAR( b, 2.41321, 0.001 );
QGSCOMPARENEAR( c, 773810, 1 );
QGSCOMPARENEAR( d, 2.4137, 0.001 );
QGSCOMPARENEAR( e, -4.1798, 0.001 );
QGSCOMPARENEAR( f, 3.35331e+06, 1e+03 );

mComposition->setGenerateWorldFile( false );
mComposerMap->setMapRotation( 0.0 );

}
#endif


void TestQgsLayoutMap::mapPolygonVertices()
Expand Down

0 comments on commit 56383e4

Please sign in to comment.