QGIS API Documentation  3.0.2-Girona (307d082)
qgsmaprenderertask.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprenderertask.h
3  -------------------------
4  begin : Apr 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 "qgsannotation.h"
19 #include "qgsannotationmanager.h"
20 #include "qgsmaprenderertask.h"
21 #include "qgsmapsettingsutils.h"
22 
23 #include <QFile>
24 #include <QTextStream>
25 #include <QPrinter>
26 
27 QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat, const bool forceRaster )
28  : QgsTask( tr( "Saving as image" ) )
29  , mMapSettings( ms )
30  , mFileName( fileName )
31  , mFileFormat( fileFormat )
32  , mForceRaster( forceRaster )
33 {
34 }
35 
37  : QgsTask( tr( "Rendering to painter" ) )
38  , mMapSettings( ms )
39  , mPainter( p )
40 {
41 }
42 
43 void QgsMapRendererTask::addAnnotations( QList< QgsAnnotation * > annotations )
44 {
45  qDeleteAll( mAnnotations );
46  mAnnotations.clear();
47 
48  Q_FOREACH ( const QgsAnnotation *a, annotations )
49  {
50  mAnnotations << a->clone();
51  }
52 }
53 
54 void QgsMapRendererTask::addDecorations( const QList< QgsMapDecoration * > &decorations )
55 {
56  mDecorations = decorations;
57 }
58 
59 
61 {
62  mJobMutex.lock();
63  if ( mJob )
64  mJob->cancelWithoutBlocking();
65  mJobMutex.unlock();
66 
68 }
69 
71 {
72  QImage img;
73  std::unique_ptr< QPainter > tempPainter;
74  QPainter *destPainter = mPainter;
75  std::unique_ptr< QPrinter > printer;
76 
77  if ( mFileFormat == QStringLiteral( "PDF" ) )
78  {
79  printer.reset( new QPrinter() );
80  printer->setOutputFileName( mFileName );
81  printer->setOutputFormat( QPrinter::PdfFormat );
82  printer->setOrientation( QPrinter::Portrait );
83  // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
84  printer->setPaperSize( mMapSettings.outputSize() * 25.4 / mMapSettings.outputDpi(), QPrinter::Millimeter );
85  printer->setPageMargins( 0, 0, 0, 0, QPrinter::Millimeter );
86  printer->setResolution( mMapSettings.outputDpi() );
87 
88  if ( !mForceRaster )
89  {
90  tempPainter.reset( new QPainter( printer.get() ) );
91  destPainter = tempPainter.get();
92  }
93  }
94 
95  if ( !destPainter )
96  {
97  // save rendered map to an image file
98  img = QImage( mMapSettings.outputSize(), QImage::Format_ARGB32 );
99  if ( img.isNull() )
100  {
101  mError = ImageAllocationFail;
102  return false;
103  }
104 
105  img.setDotsPerMeterX( 1000 * mMapSettings.outputDpi() / 25.4 );
106  img.setDotsPerMeterY( 1000 * mMapSettings.outputDpi() / 25.4 );
107 
108  tempPainter.reset( new QPainter( &img ) );
109  destPainter = tempPainter.get();
110  }
111 
112  if ( !destPainter )
113  return false;
114 
115  mJobMutex.lock();
116  mJob.reset( new QgsMapRendererCustomPainterJob( mMapSettings, destPainter ) );
117  mJobMutex.unlock();
118  mJob->renderSynchronously();
119 
120  mJobMutex.lock();
121  mJob.reset( nullptr );
122  mJobMutex.unlock();
123 
124  if ( isCanceled() )
125  return false;
126 
127  QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
128  context.setPainter( destPainter );
129 
130  Q_FOREACH ( QgsMapDecoration *decoration, mDecorations )
131  {
132  decoration->render( mMapSettings, context );
133  }
134 
135  Q_FOREACH ( QgsAnnotation *annotation, mAnnotations )
136  {
137  if ( isCanceled() )
138  return false;
139 
140  if ( !annotation || !annotation->isVisible() )
141  {
142  continue;
143  }
144  if ( annotation->mapLayer() && !mMapSettings.layers().contains( annotation->mapLayer() ) )
145  {
146  continue;
147  }
148 
149  context.painter()->save();
150  context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
151 
152  double itemX, itemY;
153  if ( annotation->hasFixedMapPosition() )
154  {
155  itemX = mMapSettings.outputSize().width() * ( annotation->mapPosition().x() - mMapSettings.extent().xMinimum() ) / mMapSettings.extent().width();
156  itemY = mMapSettings.outputSize().height() * ( 1 - ( annotation->mapPosition().y() - mMapSettings.extent().yMinimum() ) / mMapSettings.extent().height() );
157  }
158  else
159  {
160  itemX = annotation->relativePosition().x() * mMapSettings.outputSize().width();
161  itemY = annotation->relativePosition().y() * mMapSettings.outputSize().height();
162  }
163 
164  context.painter()->translate( itemX, itemY );
165 
166  annotation->render( context );
167  context.painter()->restore();
168  }
169 
170  if ( !mFileName.isEmpty() )
171  {
172  destPainter->end();
173 
174  if ( mForceRaster && mFileFormat == QStringLiteral( "PDF" ) )
175  {
176  QPainter pp;
177  pp.begin( printer.get() );
178  QRectF rect( 0, 0, img.width(), img.height() );
179  pp.drawImage( rect, img, rect );
180  pp.end();
181  }
182  else if ( mFileFormat != QStringLiteral( "PDF" ) )
183  {
184  bool success = img.save( mFileName, mFileFormat.toLocal8Bit().data() );
185  if ( !success )
186  {
187  mError = ImageSaveFail;
188  return false;
189  }
190 
191  if ( mSaveWorldFile )
192  {
193  QFileInfo info = QFileInfo( mFileName );
194 
195  // build the world file name
196  QString outputSuffix = info.suffix();
197  QString worldFileName = info.absolutePath() + '/' + info.baseName() + '.'
198  + outputSuffix.at( 0 ) + outputSuffix.at( info.suffix().size() - 1 ) + 'w';
199  QFile worldFile( worldFileName );
200 
201  if ( worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
202  {
203  QTextStream stream( &worldFile );
204  stream << QgsMapSettingsUtils::worldFileContent( mMapSettings );
205  }
206  }
207  }
208  }
209 
210  return true;
211 }
212 
213 void QgsMapRendererTask::finished( bool result )
214 {
215  qDeleteAll( mAnnotations );
216  mAnnotations.clear();
217 
218  if ( result )
219  emit renderingComplete();
220  else
221  emit errorOccurred( mError );
222 }
Job implementation that renders everything sequentially using a custom painter.
bool run() override
Performs the task&#39;s operation.
bool isVisible() const
Returns true if the annotation is visible and should be rendered.
Definition: qgsannotation.h:87
Use antialiasing while drawing.
Interface for map decorations.
double y
Definition: qgspointxy.h:48
Flags flags() const
Return combination of flags used for rendering.
QList< QgsMapLayer * > layers() const
Get list of layers for map rendering The layers are stored in the reverse order of how they are rende...
bool isCanceled() const
Will return true if task should terminate ASAP.
bool hasFixedMapPosition
Definition: qgsannotation.h:66
void addDecorations(const QList< QgsMapDecoration *> &decorations)
Adds decorations to be rendered on the map.
QPointF relativePosition() const
Returns the relative position of the annotation, if it is not attached to a fixed map position...
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:47
The QgsMapSettings class contains configuration for rendering of the map.
void addAnnotations(QList< QgsAnnotation * > annotations)
Adds annotations to be rendered on the map.
QgsRectangle extent() const
Return geographical coordinates of the rectangle that should be rendered.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
void render(QgsRenderContext &context) const
Renders the annotation to a target render context.
Abstract base class for long running background tasks.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
double x
Definition: qgspointxy.h:47
QgsMapLayer * mapLayer() const
Returns the map layer associated with the annotation.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:130
virtual void cancel()
Notifies the task that it should terminate.
double outputDpi() const
Return DPI used for conversion between real world units (e.g.
void finished(bool result) override
If the task is managed by a QgsTaskManager, this will be called after the task has finished (whether ...
Contains information about the context of a rendering operation.
void errorOccurred(int error)
Emitted when map rendering failed.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsMapRendererTask(const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat=QString("PNG"), const bool forceRaster=false)
Constructor for QgsMapRendererTask to render a map to an image file.
virtual void render(const QgsMapSettings &mapSettings, QgsRenderContext &context)=0
Renders a map decoration.
QgsPointXY mapPosition
Definition: qgsannotation.h:67
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void cancel() override
Notifies the task that it should terminate.
virtual QgsAnnotation * clone() const =0
Clones the annotation, returning a new copy of the annotation reflecting the annotation&#39;s current sta...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:120
QSize outputSize() const
Return the size of the resulting map image.
void renderingComplete()
Emitted when the map rendering is successfully completed.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:149