Skip to content
Permalink
Browse files

[feature][temporal] Add expression variables for frame properties

Adds @frame_number, @frame_rate, @frame_duration, which give
the current frame number, frames per second, and length of frames
respectively
  • Loading branch information
nyalldawson committed May 8, 2020
1 parent cd1f4f3 commit 072222d2fd4850f1ae73a58817ccfd22b6c5dfda
@@ -12,7 +12,7 @@



class QgsTemporalNavigationObject : QgsTemporalController
class QgsTemporalNavigationObject : QgsTemporalController, QgsExpressionContextScopeGenerator
{
%Docstring
Implements a temporal controller based on a frame by frame navigation and animation.
@@ -153,6 +153,9 @@ Sets whether the animation should ``loop`` after hitting the end or start frame.
.. seealso:: :py:func:`isLooping`
%End

virtual QgsExpressionContextScope *createExpressionContextScope() const /Factory/;


signals:

void stateChanged( AnimationState state );
@@ -776,6 +776,10 @@ void QgsExpression::initVariableHelp()
sVariableHelpTexts()->insert( QStringLiteral( "map_end_time" ), QCoreApplication::translate( "variable_help", "End of the map's temporal time range (as a datetime value)" ) );
sVariableHelpTexts()->insert( QStringLiteral( "map_interval" ), QCoreApplication::translate( "variable_help", "Duration of the map's temporal time range (as an interval value)" ) );

sVariableHelpTexts()->insert( QStringLiteral( "frame_rate" ), QCoreApplication::translate( "variable_help", "Number of frames per second during animation playback" ) );
sVariableHelpTexts()->insert( QStringLiteral( "frame_number" ), QCoreApplication::translate( "variable_help", "Current frame number during animation playback" ) );
sVariableHelpTexts()->insert( QStringLiteral( "frame_duration" ), QCoreApplication::translate( "variable_help", "Temporal duration of each animation frame (as an interval value)" ) );

// vector tile layer variables
sVariableHelpTexts()->insert( QStringLiteral( "zoom_level" ), QCoreApplication::translate( "variable_help", "Zoom level of the tile that is being rendered (derived from the current map scale). Normally in interval [0, 20]." ) );

@@ -69,6 +69,15 @@ void QgsTemporalNavigationObject::setLooping( bool loopAnimation )
mLoopAnimation = loopAnimation;
}

QgsExpressionContextScope *QgsTemporalNavigationObject::createExpressionContextScope() const
{
std::unique_ptr< QgsExpressionContextScope > scope = qgis::make_unique< QgsExpressionContextScope >( QStringLiteral( "temporal" ) );
scope->setVariable( QStringLiteral( "frame_rate" ), mFramesPerSecond, true );
scope->setVariable( QStringLiteral( "frame_number" ), mCurrentFrameNumber, true );
scope->setVariable( QStringLiteral( "frame_duration" ), mFrameDuration, true );
return scope.release();
}

QgsDateTimeRange QgsTemporalNavigationObject::dateTimeRangeForFrameNumber( long long frame ) const
{
const QDateTime start = mTemporalExtents.begin();
@@ -23,6 +23,7 @@
#include "qgsrange.h"
#include "qgsinterval.h"
#include "qgstemporalcontroller.h"
#include "qgsexpressioncontextscopegenerator.h"

#include <QList>
#include <QTimer>
@@ -35,7 +36,7 @@ class QgsMapLayer;
*
* \since QGIS 3.14
*/
class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController
class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController, public QgsExpressionContextScopeGenerator
{
Q_OBJECT

@@ -166,6 +167,8 @@ class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController
*/
void setLooping( bool loop );

QgsExpressionContextScope *createExpressionContextScope() const override SIP_FACTORY;

signals:

/**
@@ -554,8 +554,12 @@ void QgsMapCanvas::refreshMap()
expressionContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() )
<< QgsExpressionContextUtils::atlasScope( nullptr )
<< QgsExpressionContextUtils::mapSettingsScope( mSettings )
<< defaultExpressionContextScope()
<< QgsExpressionContextUtils::mapSettingsScope( mSettings );
if ( QgsExpressionContextScopeGenerator *generator = dynamic_cast< QgsExpressionContextScopeGenerator * >( mController ) )
{
expressionContext << generator->createExpressionContextScope();
}
expressionContext << defaultExpressionContextScope()
<< new QgsExpressionContextScope( mExpressionContextScope );

mSettings.setExpressionContext( expressionContext );
@@ -27,6 +27,7 @@
#include "qgsstyle.h"
#include "qgslogger.h"
#include "qgsexpressioncontextutils.h"
#include "qgstemporalcontroller.h"

#include "qgssymbolselectordialog.h"
#include "qgsexpressionbuilderdialog.h"
@@ -1211,6 +1212,10 @@ QgsExpressionContext QgsCategorizedSymbolRendererWidget::createExpressionContext
{
expContext << QgsExpressionContextUtils::mapSettingsScope( mContext.mapCanvas()->mapSettings() )
<< new QgsExpressionContextScope( mContext.mapCanvas()->expressionContextScope() );
if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( mContext.mapCanvas()->temporalController() ) )
{
expContext << generator->createExpressionContextScope();
}
}
else
{
@@ -51,7 +51,7 @@
#include "qgsprocessingguiregistry.h"
#include "qgsprocessingcontext.h"
#include "qgsprocessingwidgetwrapper.h"

#include "qgstemporalcontroller.h"



@@ -423,6 +423,10 @@ QgsExpressionContext QgsGraduatedSymbolRendererWidget::createExpressionContext()
{
expContext << QgsExpressionContextUtils::mapSettingsScope( mContext.mapCanvas()->mapSettings() )
<< new QgsExpressionContextScope( mContext.mapCanvas()->expressionContextScope() );
if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( mContext.mapCanvas()->temporalController() ) )
{
expContext << generator->createExpressionContextScope();
}
}
else
{
@@ -16,6 +16,7 @@
#include "qgsheatmaprenderer.h"
#include "qgsrendererregistry.h"
#include "qgsexpressioncontextutils.h"
#include "qgstemporalcontroller.h"

#include "qgssymbol.h"

@@ -45,6 +46,10 @@ QgsExpressionContext QgsHeatmapRendererWidget::createExpressionContext() const
{
expContext << QgsExpressionContextUtils::mapSettingsScope( mContext.mapCanvas()->mapSettings() )
<< new QgsExpressionContextScope( mContext.mapCanvas()->expressionContextScope() );
if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( mContext.mapCanvas()->temporalController() ) )
{
expContext << generator->createExpressionContextScope();
}
}
else
{
@@ -40,6 +40,7 @@
#include "qgsvectorlayer.h"
#include "qgsexpressioncontextutils.h"
#include "qgsmasksymbollayerwidget.h"
#include "qgstemporalcontroller.h"

static bool _initWidgetFunction( const QString &name, QgsSymbolLayerWidgetFunc f )
{
@@ -238,6 +239,10 @@ QgsExpressionContext QgsLayerPropertiesWidget::createExpressionContext() const
{
expContext << QgsExpressionContextUtils::mapSettingsScope( mContext.mapCanvas()->mapSettings() )
<< new QgsExpressionContextScope( mContext.mapCanvas()->expressionContextScope() );
if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( mContext.mapCanvas()->temporalController() ) )
{
expContext << generator->createExpressionContextScope();
}
}
else
{
@@ -26,6 +26,7 @@
#include "qgsproject.h"
#include "qgsexpressioncontextutils.h"
#include "qgssymbollayerutils.h"
#include "qgstemporalcontroller.h"

#include <QMessageBox>
#include <QInputDialog>
@@ -410,6 +411,11 @@ QgsExpressionContext QgsDataDefinedValueDialog::createExpressionContext() const
{
expContext << QgsExpressionContextUtils::mapSettingsScope( mContext.mapCanvas()->mapSettings() )
<< new QgsExpressionContextScope( mContext.mapCanvas()->expressionContextScope() );

if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( mContext.mapCanvas()->temporalController() ) )
{
expContext << generator->createExpressionContextScope();
}
}
else
{
@@ -17,6 +17,7 @@
#include "qgsmessagebar.h"
#include "qgsproject.h"
#include "qgsexpressioncontextutils.h"
#include "qgstemporalcontroller.h"

QgsSymbolWidgetContext::QgsSymbolWidgetContext( const QgsSymbolWidgetContext &other )
: mMapCanvas( other.mMapCanvas )
@@ -99,6 +100,11 @@ QList<QgsExpressionContextScope *> QgsSymbolWidgetContext::globalProjectAtlasMap
scopes << QgsExpressionContextUtils::mapSettingsScope( mMapCanvas->mapSettings() )
<< mMapCanvas->defaultExpressionContextScope()
<< new QgsExpressionContextScope( mMapCanvas->expressionContextScope() );

if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( mMapCanvas->temporalController() ) )
{
scopes << generator->createExpressionContextScope();
}
}
else
{
@@ -41,6 +41,7 @@ class TestQgsTemporalNavigationObject : public QObject
void animationState();
void temporalExtents();
void frameSettings();
void expressionContext();

private:
QgsTemporalNavigationObject *navigationObject = nullptr;
@@ -164,5 +165,23 @@ void TestQgsTemporalNavigationObject::frameSettings()

}

void TestQgsTemporalNavigationObject::expressionContext()
{
QgsTemporalNavigationObject object;
QgsDateTimeRange range = QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 0, 0 ) )
);
object.setTemporalExtents( range );
object.setFrameDuration( QgsInterval( 1, QgsUnitTypes::TemporalHours ) );
object.setCurrentFrameNumber( 1 );
object.setFramesPerSecond( 30 );

std::unique_ptr< QgsExpressionContextScope > scope( object.createExpressionContextScope() );
QCOMPARE( scope->variable( QStringLiteral( "frame_rate" ) ).toDouble(), 30.0 );
QCOMPARE( scope->variable( QStringLiteral( "frame_duration" ) ).value< QgsInterval >().seconds(), 3600.0 );
QCOMPARE( scope->variable( QStringLiteral( "frame_number" ) ).toInt(), 1 );
}

QGSTEST_MAIN( TestQgsTemporalNavigationObject )
#include "testqgstemporalnavigationobject.moc"

0 comments on commit 072222d

Please sign in to comment.
You can’t perform that action at this time.