QGIS API Documentation  3.17.0-Master (a035f434f4)
qgstemporalutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstemporalutils.cpp
3  -----------------------
4  Date : March 2020
5  Copyright : (C) 2020 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgstemporalutils.h"
17 #include "qgsproject.h"
19 #include "qgsrasterlayer.h"
20 #include "qgsmeshlayer.h"
21 #include "qgsvectorlayer.h"
26 #include "qgsmapdecoration.h"
27 #include "qgsmapsettings.h"
30 
32 {
33  const QMap<QString, QgsMapLayer *> &mapLayers = project->mapLayers();
34  QgsMapLayer *currentLayer = nullptr;
35 
36  QDateTime minDate;
37  QDateTime maxDate;
38 
39  for ( QMap<QString, QgsMapLayer *>::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it )
40  {
41  currentLayer = it.value();
42 
43  if ( !currentLayer->temporalProperties() || !currentLayer->temporalProperties()->isActive() )
44  continue;
45  QgsDateTimeRange layerRange = currentLayer->temporalProperties()->calculateTemporalExtent( currentLayer );
46 
47  if ( layerRange.begin().isValid() && ( !minDate.isValid() || layerRange.begin() < minDate ) )
48  minDate = layerRange.begin();
49  if ( layerRange.end().isValid() && ( !maxDate.isValid() || layerRange.end() > maxDate ) )
50  maxDate = layerRange.end();
51  }
52 
53  return QgsDateTimeRange( minDate, maxDate );
54 }
55 
56 bool QgsTemporalUtils::exportAnimation( const QgsMapSettings &mapSettings, const QgsTemporalUtils::AnimationExportSettings &settings, QString &error, QgsFeedback *feedback )
57 {
58  if ( settings.fileNameTemplate.isEmpty() )
59  {
60  error = QObject::tr( "Filename template is empty" );
61  return false;
62  }
63  int numberOfDigits = settings.fileNameTemplate.count( QLatin1Char( '#' ) );
64  if ( numberOfDigits < 0 )
65  {
66  error = QObject::tr( "Wrong filename template format (must contain #)" );
67  return false;
68  }
69  const QString token( numberOfDigits, QLatin1Char( '#' ) );
70  if ( !settings.fileNameTemplate.contains( token ) )
71  {
72  error = QObject::tr( "Filename template must contain all # placeholders in one continuous group." );
73  return false;
74  }
75  if ( !QDir().mkpath( settings.outputDirectory ) )
76  {
77  error = QObject::tr( "Output directory creation failure." );
78  return false;
79  }
80 
82  navigator.setTemporalExtents( settings.animationRange );
83  navigator.setFrameDuration( settings.frameDuration );
84  QgsMapSettings ms = mapSettings;
85  const QgsExpressionContext context = ms.expressionContext();
86 
87  const long long totalFrames = navigator.totalFrameCount();
88  long long currentFrame = 0;
89 
90  while ( currentFrame < totalFrames )
91  {
92  if ( feedback )
93  {
94  if ( feedback->isCanceled() )
95  {
96  error = QObject::tr( "Export canceled" );
97  return false;
98  }
99  feedback->setProgress( currentFrame / static_cast<double>( totalFrames ) * 100 );
100  }
101  ++currentFrame;
102 
103  navigator.setCurrentFrameNumber( currentFrame );
104 
105  ms.setIsTemporal( true );
106  ms.setTemporalRange( navigator.dateTimeRangeForFrameNumber( currentFrame ) );
107 
108  QgsExpressionContext frameContext = context;
109  frameContext.appendScope( navigator.createExpressionContextScope() );
111  ms.setExpressionContext( frameContext );
112 
113  QString fileName( settings.fileNameTemplate );
114  const QString frameNoPaddedLeft( QStringLiteral( "%1" ).arg( currentFrame, numberOfDigits, 10, QChar( '0' ) ) ); // e.g. 0001
115  fileName.replace( token, frameNoPaddedLeft );
116  const QString path = QDir( settings.outputDirectory ).filePath( fileName );
117 
118  QImage img = QImage( ms.outputSize(), ms.outputImageFormat() );
119  img.setDotsPerMeterX( 1000 * ms.outputDpi() / 25.4 );
120  img.setDotsPerMeterY( 1000 * ms.outputDpi() / 25.4 );
121  img.fill( ms.backgroundColor().rgb() );
122 
123  QPainter p( &img );
124  QgsMapRendererCustomPainterJob job( ms, &p );
125  job.start();
126  job.waitForFinished();
127 
129  context.setPainter( &p );
130 
131  const auto constMDecorations = settings.decorations;
132  for ( QgsMapDecoration *decoration : constMDecorations )
133  {
134  decoration->render( ms, context );
135  }
136 
137  p.end();
138 
139  img.save( path );
140  }
141 
142  return true;
143 }
long long totalFrameCount() const
Returns the total number of frames for the navigation.
Base class for all map layer types.
Definition: qgsmaplayer.h:83
Job implementation that renders everything sequentially using a custom painter.
QgsDateTimeRange animationRange
Dictates the overall temporal range of the animation.
const QgsExpressionContext & expressionContext() const
Gets the expression context.
bool isActive() const
Returns true if the temporal property is active.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QString outputDirectory
Destination directory for created image files.
Interface for map decorations.
void setCurrentFrameNumber(long long frame)
Sets the current animation frame number.
virtual QgsDateTimeRange calculateTemporalExtent(QgsMapLayer *layer) const
Attempts to calculate the overall temporal extent for the specified layer, using the settings defined...
QColor backgroundColor() const
Gets the background color of the map.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:62
QgsDateTimeRange dateTimeRangeForFrameNumber(long long frame) const
Calculates the temporal range associated with a particular animation frame.
void setFrameDuration(QgsInterval duration)
Sets the frame duration, which dictates the temporal length of each frame in the animation.
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
QList< QgsMapDecoration * > decorations
List of decorations to draw onto exported frames.
The QgsMapSettings class contains configuration for rendering of the map.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:43
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer&#39;s temporal properties.
Definition: qgsmaplayer.h:1229
void start() override
Start the rendering job and immediately return.
static QgsDateTimeRange calculateTemporalRangeForProject(QgsProject *project)
Calculates the temporal range for a project.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Implements a temporal controller based on a frame by frame navigation and animation.
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:94
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
static bool exportAnimation(const QgsMapSettings &mapSettings, const AnimationExportSettings &settings, QString &error, QgsFeedback *feedback=nullptr)
Exports animation frames by rendering the map to multiple destination images.
Contains settings relating to exporting animations.
double outputDpi() const
Returns DPI used for conversion between real world units (e.g.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Contains information about the context of a rendering operation.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
void setIsTemporal(bool enabled)
Sets whether the temporal range is enabled (i.e.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QString fileNameTemplate
The filename template for exporting the frames.
QgsInterval frameDuration
Duration of individual export frames.
void waitForFinished() override
Block until the job has finished.
QSize outputSize() const
Returns the size of the resulting map image.
void setTemporalExtents(const QgsDateTimeRange &extents)
Sets the navigation temporal extents, which dictate the earliest and latest date time possible in the...