QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  const auto constAnnotations = annotations;
49  for ( const QgsAnnotation *a : constAnnotations )
50  {
51  mAnnotations << a->clone();
52  }
53 }
54 
55 void QgsMapRendererTask::addDecorations( const QList< QgsMapDecoration * > &decorations )
56 {
57  mDecorations = decorations;
58 }
59 
60 
62 {
63  mJobMutex.lock();
64  if ( mJob )
65  mJob->cancelWithoutBlocking();
66  mJobMutex.unlock();
67 
69 }
70 
72 {
73  QImage img;
74  std::unique_ptr< QPainter > tempPainter;
75  QPainter *destPainter = mPainter;
76 
77 #ifndef QT_NO_PRINTER
78  std::unique_ptr< QPrinter > printer;
79 #endif // ! QT_NO_PRINTER
80 
81  if ( mFileFormat == QStringLiteral( "PDF" ) )
82  {
83 #ifndef QT_NO_PRINTER
84  printer.reset( new QPrinter() );
85  printer->setOutputFileName( mFileName );
86  printer->setOutputFormat( QPrinter::PdfFormat );
87  printer->setOrientation( QPrinter::Portrait );
88  // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
89  printer->setPaperSize( mMapSettings.outputSize() * 25.4 / mMapSettings.outputDpi(), QPrinter::Millimeter );
90  printer->setPageMargins( 0, 0, 0, 0, QPrinter::Millimeter );
91  printer->setResolution( mMapSettings.outputDpi() );
92 
93  if ( !mForceRaster )
94  {
95  tempPainter.reset( new QPainter( printer.get() ) );
96  destPainter = tempPainter.get();
97  }
98 #else
99  mError = ImageUnsupportedFormat;
100  return false;
101 #endif // ! QT_NO_PRINTER
102  }
103 
104  if ( !destPainter )
105  {
106  // save rendered map to an image file
107  img = QImage( mMapSettings.outputSize(), QImage::Format_ARGB32 );
108  if ( img.isNull() )
109  {
110  mError = ImageAllocationFail;
111  return false;
112  }
113 
114  img.setDotsPerMeterX( 1000 * mMapSettings.outputDpi() / 25.4 );
115  img.setDotsPerMeterY( 1000 * mMapSettings.outputDpi() / 25.4 );
116 
117  tempPainter.reset( new QPainter( &img ) );
118  destPainter = tempPainter.get();
119  }
120 
121  if ( !destPainter )
122  return false;
123 
124  mJobMutex.lock();
125  mJob.reset( new QgsMapRendererCustomPainterJob( mMapSettings, destPainter ) );
126  mJobMutex.unlock();
127  mJob->renderSynchronously();
128 
129  mJobMutex.lock();
130  mJob.reset( nullptr );
131  mJobMutex.unlock();
132 
133  if ( isCanceled() )
134  return false;
135 
136  QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
137  context.setPainter( destPainter );
138 
139  const auto constMDecorations = mDecorations;
140  for ( QgsMapDecoration *decoration : constMDecorations )
141  {
142  decoration->render( mMapSettings, context );
143  }
144 
145  const auto constMAnnotations = mAnnotations;
146  for ( QgsAnnotation *annotation : constMAnnotations )
147  {
148  if ( isCanceled() )
149  return false;
150 
151  if ( !annotation || !annotation->isVisible() )
152  {
153  continue;
154  }
155  if ( annotation->mapLayer() && !mMapSettings.layers().contains( annotation->mapLayer() ) )
156  {
157  continue;
158  }
159 
160  context.painter()->save();
161  context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
162 
163  double itemX, itemY;
164  if ( annotation->hasFixedMapPosition() )
165  {
166  itemX = mMapSettings.outputSize().width() * ( annotation->mapPosition().x() - mMapSettings.extent().xMinimum() ) / mMapSettings.extent().width();
167  itemY = mMapSettings.outputSize().height() * ( 1 - ( annotation->mapPosition().y() - mMapSettings.extent().yMinimum() ) / mMapSettings.extent().height() );
168  }
169  else
170  {
171  itemX = annotation->relativePosition().x() * mMapSettings.outputSize().width();
172  itemY = annotation->relativePosition().y() * mMapSettings.outputSize().height();
173  }
174 
175  context.painter()->translate( itemX, itemY );
176 
177  annotation->render( context );
178  context.painter()->restore();
179  }
180 
181  if ( !mFileName.isEmpty() )
182  {
183  destPainter->end();
184 
185  if ( mForceRaster && mFileFormat == QStringLiteral( "PDF" ) )
186  {
187 #ifndef QT_NO_PRINTER
188  QPainter pp;
189  pp.begin( printer.get() );
190  QRectF rect( 0, 0, img.width(), img.height() );
191  pp.drawImage( rect, img, rect );
192  pp.end();
193 #else
194  mError = ImageUnsupportedFormat;
195  return false;
196 #endif // !QT_NO_PRINTER
197  }
198  else if ( mFileFormat != QStringLiteral( "PDF" ) )
199  {
200  bool success = img.save( mFileName, mFileFormat.toLocal8Bit().data() );
201  if ( !success )
202  {
203  mError = ImageSaveFail;
204  return false;
205  }
206 
207  if ( mSaveWorldFile )
208  {
209  QFileInfo info = QFileInfo( mFileName );
210 
211  // build the world file name
212  QString outputSuffix = info.suffix();
213  QString worldFileName = info.absolutePath() + '/' + info.baseName() + '.'
214  + outputSuffix.at( 0 ) + outputSuffix.at( info.suffix().size() - 1 ) + 'w';
215  QFile worldFile( worldFileName );
216 
217  if ( worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
218  {
219  QTextStream stream( &worldFile );
220  stream << QgsMapSettingsUtils::worldFileContent( mMapSettings );
221  }
222  }
223  }
224  }
225 
226  return true;
227 }
228 
229 void QgsMapRendererTask::finished( bool result )
230 {
231  qDeleteAll( mAnnotations );
232  mAnnotations.clear();
233 
234  if ( result )
235  emit renderingComplete();
236  else
237  emit errorOccurred( mError );
238 }
Job implementation that renders everything sequentially using a custom painter.
bool run() override
Performs the task&#39;s operation.
Use antialiasing while drawing.
Interface for map decorations.
Flags flags() const
Returns combination of flags used for rendering.
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
bool isCanceled() const
Will return true if task should terminate ASAP.
void addDecorations(const QList< QgsMapDecoration *> &decorations)
Adds decorations to be rendered on the map.
static QString worldFileContent(const QgsMapSettings &mapSettings)
Creates the content of a world file.
QgsMapRendererTask(const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat=QString("PNG"), bool forceRaster=false)
Constructor for QgsMapRendererTask to render a map to an image file.
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:49
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
Returns geographical coordinates of the rectangle that should be rendered.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
Abstract base class for long running background tasks.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Format is unsupported on the platform.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
virtual void cancel()
Notifies the task that it should terminate.
double outputDpi() const
Returns 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.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void cancel() override
Notifies the task that it should terminate.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QSize outputSize() const
Returns 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:209