QGIS API Documentation  3.6.0-Noosa (5873452)
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 
76 #ifndef QT_NO_PRINTER
77  std::unique_ptr< QPrinter > printer;
78 #endif // ! QT_NO_PRINTER
79 
80  if ( mFileFormat == QStringLiteral( "PDF" ) )
81  {
82 #ifndef QT_NO_PRINTER
83  printer.reset( new QPrinter() );
84  printer->setOutputFileName( mFileName );
85  printer->setOutputFormat( QPrinter::PdfFormat );
86  printer->setOrientation( QPrinter::Portrait );
87  // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
88  printer->setPaperSize( mMapSettings.outputSize() * 25.4 / mMapSettings.outputDpi(), QPrinter::Millimeter );
89  printer->setPageMargins( 0, 0, 0, 0, QPrinter::Millimeter );
90  printer->setResolution( mMapSettings.outputDpi() );
91 
92  if ( !mForceRaster )
93  {
94  tempPainter.reset( new QPainter( printer.get() ) );
95  destPainter = tempPainter.get();
96  }
97 #else
98  mError = ImageUnsupportedFormat;
99  return false;
100 #endif // ! QT_NO_PRINTER
101  }
102 
103  if ( !destPainter )
104  {
105  // save rendered map to an image file
106  img = QImage( mMapSettings.outputSize(), QImage::Format_ARGB32 );
107  if ( img.isNull() )
108  {
109  mError = ImageAllocationFail;
110  return false;
111  }
112 
113  img.setDotsPerMeterX( 1000 * mMapSettings.outputDpi() / 25.4 );
114  img.setDotsPerMeterY( 1000 * mMapSettings.outputDpi() / 25.4 );
115 
116  tempPainter.reset( new QPainter( &img ) );
117  destPainter = tempPainter.get();
118  }
119 
120  if ( !destPainter )
121  return false;
122 
123  mJobMutex.lock();
124  mJob.reset( new QgsMapRendererCustomPainterJob( mMapSettings, destPainter ) );
125  mJobMutex.unlock();
126  mJob->renderSynchronously();
127 
128  mJobMutex.lock();
129  mJob.reset( nullptr );
130  mJobMutex.unlock();
131 
132  if ( isCanceled() )
133  return false;
134 
135  QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
136  context.setPainter( destPainter );
137 
138  Q_FOREACH ( QgsMapDecoration *decoration, mDecorations )
139  {
140  decoration->render( mMapSettings, context );
141  }
142 
143  Q_FOREACH ( QgsAnnotation *annotation, mAnnotations )
144  {
145  if ( isCanceled() )
146  return false;
147 
148  if ( !annotation || !annotation->isVisible() )
149  {
150  continue;
151  }
152  if ( annotation->mapLayer() && !mMapSettings.layers().contains( annotation->mapLayer() ) )
153  {
154  continue;
155  }
156 
157  context.painter()->save();
158  context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
159 
160  double itemX, itemY;
161  if ( annotation->hasFixedMapPosition() )
162  {
163  itemX = mMapSettings.outputSize().width() * ( annotation->mapPosition().x() - mMapSettings.extent().xMinimum() ) / mMapSettings.extent().width();
164  itemY = mMapSettings.outputSize().height() * ( 1 - ( annotation->mapPosition().y() - mMapSettings.extent().yMinimum() ) / mMapSettings.extent().height() );
165  }
166  else
167  {
168  itemX = annotation->relativePosition().x() * mMapSettings.outputSize().width();
169  itemY = annotation->relativePosition().y() * mMapSettings.outputSize().height();
170  }
171 
172  context.painter()->translate( itemX, itemY );
173 
174  annotation->render( context );
175  context.painter()->restore();
176  }
177 
178  if ( !mFileName.isEmpty() )
179  {
180  destPainter->end();
181 
182  if ( mForceRaster && mFileFormat == QStringLiteral( "PDF" ) )
183  {
184 #ifndef QT_NO_PRINTER
185  QPainter pp;
186  pp.begin( printer.get() );
187  QRectF rect( 0, 0, img.width(), img.height() );
188  pp.drawImage( rect, img, rect );
189  pp.end();
190 #else
191  mError = ImageUnsupportedFormat;
192  return false;
193 #endif // !QT_NO_PRINTER
194  }
195  else if ( mFileFormat != QStringLiteral( "PDF" ) )
196  {
197  bool success = img.save( mFileName, mFileFormat.toLocal8Bit().data() );
198  if ( !success )
199  {
200  mError = ImageSaveFail;
201  return false;
202  }
203 
204  if ( mSaveWorldFile )
205  {
206  QFileInfo info = QFileInfo( mFileName );
207 
208  // build the world file name
209  QString outputSuffix = info.suffix();
210  QString worldFileName = info.absolutePath() + '/' + info.baseName() + '.'
211  + outputSuffix.at( 0 ) + outputSuffix.at( info.suffix().size() - 1 ) + 'w';
212  QFile worldFile( worldFileName );
213 
214  if ( worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
215  {
216  QTextStream stream( &worldFile );
217  stream << QgsMapSettingsUtils::worldFileContent( mMapSettings );
218  }
219  }
220  }
221  }
222 
223  return true;
224 }
225 
226 void QgsMapRendererTask::finished( bool result )
227 {
228  qDeleteAll( mAnnotations );
229  mAnnotations.clear();
230 
231  if ( result )
232  emit renderingComplete();
233  else
234  emit errorOccurred( mError );
235 }
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:89
Use antialiasing while drawing.
Interface for map decorations.
double y
Definition: qgspointxy.h:48
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.
bool hasFixedMapPosition
Definition: qgsannotation.h:68
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.
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
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.
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.
virtual void render(const QgsMapSettings &mapSettings, QgsRenderContext &context)=0
Renders a map decoration.
QgsPointXY mapPosition
Definition: qgsannotation.h:69
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: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