Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][temporal] Add off and fixed range modes to temporal navigation
  • Loading branch information
nirvn committed May 15, 2020
1 parent db1062c commit ba5b416
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 23 deletions.
3 changes: 3 additions & 0 deletions images/images.qrc
Expand Up @@ -712,6 +712,9 @@
<file>themes/default/mTaskRunning.svg</file>
<file>themes/default/mTaskTerminated.svg</file>
<file>themes/default/mTaskCancel.svg</file>
<file>themes/default/mTemporalNavigationOff.svg</file>
<file>themes/default/mTemporalNavigationFixedRange.svg</file>
<file>themes/default/mTemporalNavigationAnimated.svg</file>
<file>themes/default/providerGdal.svg</file>
<file>themes/default/providerGrass.svg</file>
<file>themes/default/providerQgis.svg</file>
Expand Down
1 change: 1 addition & 0 deletions images/themes/default/mTemporalNavigationAnimated.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions images/themes/default/mTemporalNavigationFixedRange.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions images/themes/default/mTemporalNavigationOff.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions python/core/auto_generated/qgstemporalnavigationobject.sip.in
Expand Up @@ -30,6 +30,13 @@ Implements a temporal controller based on a frame by frame navigation and animat
Constructor for QgsTemporalNavigationObject, with the specified ``parent`` object.
%End

enum NavigationMode
{
NavigationOff,
Animated,
FixedRange,
};

enum AnimationState
{
Forward,
Expand All @@ -49,6 +56,20 @@ Sets the current animation ``state``.
Returns the current animation state.

.. seealso:: :py:func:`setAnimationState`
%End

void setNavigationMode( const NavigationMode mode );
%Docstring
Sets the temporal navigation ``mode``.

.. seealso:: :py:func:`navigationMode`
%End

NavigationMode navigationMode() const;
%Docstring
Returns the currenttemporal navigation mode.

.. seealso:: :py:func:`setNavigationMode`
%End

void setTemporalExtents( const QgsDateTimeRange &extents );
Expand Down Expand Up @@ -175,6 +196,11 @@ Sets whether the animation should ``loop`` after hitting the end or start frame.
void stateChanged( AnimationState state );
%Docstring
Emitted whenever the animation ``state`` changes.
%End

void navigationModeChanged( NavigationMode mode );
%Docstring
Emitted whenever the navigation ``mode`` changes.
%End

public slots:
Expand Down
46 changes: 40 additions & 6 deletions src/core/qgstemporalnavigationobject.cpp
Expand Up @@ -104,16 +104,50 @@ QgsDateTimeRange QgsTemporalNavigationObject::dateTimeRangeForFrameNumber( long
return QgsDateTimeRange( frameStart, mTemporalExtents.end(), true, false );
}

void QgsTemporalNavigationObject::setNavigationMode( const NavigationMode mode )
{
if ( mNavigationMode == mode )
return;

mNavigationMode = mode;
emit navigationModeChanged( mode );

switch ( mNavigationMode )
{
case Animated:
emit updateTemporalRange( dateTimeRangeForFrameNumber( mCurrentFrameNumber ) );
break;
case FixedRange:
emit updateTemporalRange( mTemporalExtents );
break;
case NavigationOff:
emit updateTemporalRange( QgsDateTimeRange() );
break;
}
}

void QgsTemporalNavigationObject::setTemporalExtents( const QgsDateTimeRange &temporalExtents )
{
mTemporalExtents = temporalExtents;
int currentFrameNmber = mCurrentFrameNumber;
setCurrentFrameNumber( 0 );

//Force to emit signal if the current frame number doesn't change
if ( currentFrameNmber == mCurrentFrameNumber )
emit updateTemporalRange( dateTimeRangeForFrameNumber( 0 ) );

switch ( mNavigationMode )
{
case Animated:
{
int currentFrameNmber = mCurrentFrameNumber;
setCurrentFrameNumber( 0 );

//Force to emit signal if the current frame number doesn't change
if ( currentFrameNmber == mCurrentFrameNumber )
emit updateTemporalRange( dateTimeRangeForFrameNumber( 0 ) );
break;
}
case FixedRange:
emit updateTemporalRange( mTemporalExtents );
break;
case NavigationOff:
break;
}
}

QgsDateTimeRange QgsTemporalNavigationObject::temporalExtents() const
Expand Down
28 changes: 28 additions & 0 deletions src/core/qgstemporalnavigationobject.h
Expand Up @@ -47,6 +47,13 @@ class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController, pu
*/
QgsTemporalNavigationObject( QObject *parent SIP_TRANSFERTHIS = nullptr );

enum NavigationMode
{
NavigationOff, //!< Temporal navigation is disabled
Animated, //!< Temporal navigation relies on frames within a datetime range
FixedRange, //!< Temporal navigation relies on a fixed datetime range
};

//! Represents the current animation state.
enum AnimationState
{
Expand All @@ -69,6 +76,20 @@ class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController, pu
*/
AnimationState animationState() const;

/**
* Sets the temporal navigation \a mode.
*
* \see navigationMode()
*/
void setNavigationMode( const NavigationMode mode );

/**
* Returns the currenttemporal navigation mode.
*
* \see setNavigationMode()
*/
NavigationMode navigationMode() const { return mNavigationMode; }

/**
* Sets the navigation temporal \a extents, which dictate the earliest
* and latest date time possible in the animation.
Expand Down Expand Up @@ -190,6 +211,11 @@ class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController, pu
*/
void stateChanged( AnimationState state );

/**
* Emitted whenever the navigation \a mode changes.
*/
void navigationModeChanged( NavigationMode mode );

public slots:

/**
Expand Down Expand Up @@ -255,6 +281,8 @@ class CORE_EXPORT QgsTemporalNavigationObject : public QgsTemporalController, pu
//! The controller temporal navigation extent range.
QgsDateTimeRange mTemporalExtents;

NavigationMode mNavigationMode = Animated;

//! The current set frame value
long long mCurrentFrameNumber = 0;

Expand Down
1 change: 1 addition & 0 deletions src/gui/qgsmapcanvas.cpp
Expand Up @@ -798,6 +798,7 @@ void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange )
return;

mSettings.setTemporalRange( dateTimeRange );
mSettings.setIsTemporal( dateTimeRange.begin().isValid() || dateTimeRange.end().isValid() );

emit temporalRangeChanged();

Expand Down
100 changes: 88 additions & 12 deletions src/gui/qgstemporalcontrollerwidget.cpp
Expand Up @@ -19,7 +19,6 @@
#include "qgsgui.h"
#include "qgsproject.h"
#include "qgsprojecttimesettings.h"
#include "qgstemporalnavigationobject.h"
#include "qgstemporalmapsettingswidget.h"
#include "qgstemporalutils.h"
#include "qgsmaplayertemporalproperties.h"
Expand All @@ -40,6 +39,12 @@ QgsTemporalControllerWidget::QgsTemporalControllerWidget( QWidget *parent )
connect( mRewindButton, &QPushButton::clicked, mNavigationObject, &QgsTemporalNavigationObject::rewindToStart );
connect( mLoopingCheckBox, &QCheckBox::toggled, this, [ = ]( bool state ) { mNavigationObject->setLooping( state ); } );

setWidgetStateFromNavigationMode( mNavigationObject->navigationMode() );
connect( mNavigationObject, &QgsTemporalNavigationObject::navigationModeChanged, this, &QgsTemporalControllerWidget::setWidgetStateFromNavigationMode );
connect( mNavigationOff, &QPushButton::clicked, this, &QgsTemporalControllerWidget::mNavigationOff_clicked );
connect( mNavigationFixedRange, &QPushButton::clicked, this, &QgsTemporalControllerWidget::mNavigationFixedRange_clicked );
connect( mNavigationAnimated, &QPushButton::clicked, this, &QgsTemporalControllerWidget::mNavigationAnimated_clicked );

connect( mNavigationObject, &QgsTemporalNavigationObject::stateChanged, this, [ = ]( QgsTemporalNavigationObject::AnimationState state )
{
mForwardButton->setChecked( state == QgsTemporalNavigationObject::Forward );
Expand All @@ -49,11 +54,11 @@ QgsTemporalControllerWidget::QgsTemporalControllerWidget( QWidget *parent )

connect( mStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsTemporalControllerWidget::startEndDateTime_changed );
connect( mEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsTemporalControllerWidget::startEndDateTime_changed );
connect( mSpinBox, qgis::overload<double>::of( &QDoubleSpinBox::valueChanged ), this, &QgsTemporalControllerWidget::updateFrameDuration );
connect( mStepSpinBox, qgis::overload<double>::of( &QDoubleSpinBox::valueChanged ), this, &QgsTemporalControllerWidget::updateFrameDuration );
connect( mTimeStepsComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsTemporalControllerWidget::updateFrameDuration );
connect( mSlider, &QSlider::valueChanged, this, &QgsTemporalControllerWidget::timeSlider_valueChanged );

mSpinBox->setClearValue( 1 );
mStepSpinBox->setClearValue( 1 );

connect( mNavigationObject, &QgsTemporalNavigationObject::updateTemporalRange, this, &QgsTemporalControllerWidget::updateSlider );

Expand Down Expand Up @@ -101,10 +106,10 @@ QgsTemporalControllerWidget::QgsTemporalControllerWidget( QWidget *parent )
// TODO: might want to choose an appropriate default unit based on the range
mTimeStepsComboBox->setCurrentIndex( mTimeStepsComboBox->findData( QgsUnitTypes::TemporalHours ) );

mSpinBox->setMinimum( 0.0000001 );
mSpinBox->setMaximum( std::numeric_limits<int>::max() );
mSpinBox->setSingleStep( 1 );
mSpinBox->setValue( 1 );
mStepSpinBox->setMinimum( 0.0000001 );
mStepSpinBox->setMaximum( std::numeric_limits<int>::max() );
mStepSpinBox->setSingleStep( 1 );
mStepSpinBox->setValue( 1 );

mForwardButton->setToolTip( tr( "Play" ) );
mBackButton->setToolTip( tr( "Reverse" ) );
Expand Down Expand Up @@ -137,7 +142,7 @@ void QgsTemporalControllerWidget::updateFrameDuration()

// save new settings into project
QgsProject::instance()->timeSettings()->setTimeStepUnit( static_cast< QgsUnitTypes::TemporalUnit>( mTimeStepsComboBox->currentData().toInt() ) );
QgsProject::instance()->timeSettings()->setTimeStep( mSpinBox->value() );
QgsProject::instance()->timeSettings()->setTimeStep( mStepSpinBox->value() );

mNavigationObject->setFrameDuration( QgsInterval( QgsProject::instance()->timeSettings()->timeStep(),
QgsProject::instance()->timeSettings()->timeStepUnit() ) );
Expand All @@ -148,9 +153,15 @@ void QgsTemporalControllerWidget::setWidgetStateFromProject()
{
mBlockSettingUpdates++;
mTimeStepsComboBox->setCurrentIndex( mTimeStepsComboBox->findData( QgsProject::instance()->timeSettings()->timeStepUnit() ) );
mSpinBox->setValue( QgsProject::instance()->timeSettings()->timeStep() );
mStepSpinBox->setValue( QgsProject::instance()->timeSettings()->timeStep() );
mBlockSettingUpdates--;

bool ok = false;
QgsTemporalNavigationObject::NavigationMode mode = static_cast< QgsTemporalNavigationObject::NavigationMode>( QgsProject::instance()->readNumEntry( QStringLiteral( "TemporalControllerWidget" ),
QStringLiteral( "/NavigationMode" ), 0, &ok ) );
if ( ok )
mNavigationObject->setNavigationMode( mode );

const QString startString = QgsProject::instance()->readEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/StartDateTime" ) );
const QString endString = QgsProject::instance()->readEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/EndDateTime" ) );
if ( !startString.isEmpty() && !endString.isEmpty() )
Expand All @@ -169,6 +180,58 @@ void QgsTemporalControllerWidget::setWidgetStateFromProject()
mNavigationObject->setTemporalRangeCumulative( QgsProject::instance()->timeSettings()->isTemporalRangeCumulative() );
}

void QgsTemporalControllerWidget::mNavigationOff_clicked()
{
QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/NavigationMode" ),
static_cast<int>( QgsTemporalNavigationObject::NavigationOff ) );

mNavigationObject->setNavigationMode( QgsTemporalNavigationObject::NavigationOff );
setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::NavigationOff );
}

void QgsTemporalControllerWidget::mNavigationFixedRange_clicked()
{
QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/NavigationMode" ),
static_cast<int>( QgsTemporalNavigationObject::FixedRange ) );

mNavigationObject->setNavigationMode( QgsTemporalNavigationObject::FixedRange );
setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::FixedRange );
}

void QgsTemporalControllerWidget::mNavigationAnimated_clicked()
{
QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/NavigationMode" ),
static_cast<int>( QgsTemporalNavigationObject::Animated ) );

mNavigationObject->setNavigationMode( QgsTemporalNavigationObject::Animated );
setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::Animated );
}

void QgsTemporalControllerWidget::setWidgetStateFromNavigationMode( const QgsTemporalNavigationObject::NavigationMode mode )
{
mNavigationOff->setChecked( mode == QgsTemporalNavigationObject::NavigationOff );
mNavigationFixedRange->setChecked( mode == QgsTemporalNavigationObject::FixedRange );
mNavigationAnimated->setChecked( mode == QgsTemporalNavigationObject::Animated );

mRewindButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mPreviousButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mBackButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mStopButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mForwardButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mNextButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mFastForwardButton->setVisible( mode == QgsTemporalNavigationObject::Animated );
mSlider->setVisible( mode == QgsTemporalNavigationObject::Animated );
mLoopingCheckBox->setVisible( mode == QgsTemporalNavigationObject::Animated );
mStepLabel->setVisible( mode == QgsTemporalNavigationObject::Animated );
mStepSpinBox->setVisible( mode == QgsTemporalNavigationObject::Animated );
mTimeStepsComboBox->setVisible( mode == QgsTemporalNavigationObject::Animated );
mRangeLabel->setVisible( mode == QgsTemporalNavigationObject::FixedRange || mode == QgsTemporalNavigationObject::Animated );
mStartDateTime->setVisible( mode == QgsTemporalNavigationObject::FixedRange || mode == QgsTemporalNavigationObject::Animated );
mRangeToLabel->setVisible( mode == QgsTemporalNavigationObject::FixedRange || mode == QgsTemporalNavigationObject::Animated );
mEndDateTime->setVisible( mode == QgsTemporalNavigationObject::FixedRange || mode == QgsTemporalNavigationObject::Animated );
mSetToProjectTimeButton->setVisible( mode == QgsTemporalNavigationObject::FixedRange || mode == QgsTemporalNavigationObject::Animated );
}

void QgsTemporalControllerWidget::onLayersAdded( const QList<QgsMapLayer *> &layers )
{
if ( !mHasTemporalLayersLoaded )
Expand Down Expand Up @@ -215,9 +278,22 @@ void QgsTemporalControllerWidget::updateSlider( const QgsDateTimeRange &range )

void QgsTemporalControllerWidget::updateRangeLabel( const QgsDateTimeRange &range )
{
mCurrentRangeLabel->setText( tr( "%1 to %2" ).arg(
range.begin().toString( "yyyy-MM-dd HH:mm:ss" ),
range.end().toString( "yyyy-MM-dd HH:mm:ss" ) ) );
switch ( mNavigationObject->navigationMode() )
{
case QgsTemporalNavigationObject::Animated:
mCurrentRangeLabel->setText( tr( "Frame: %1 to %2" ).arg(
range.begin().toString( "yyyy-MM-dd HH:mm:ss" ),
range.end().toString( "yyyy-MM-dd HH:mm:ss" ) ) );
break;
case QgsTemporalNavigationObject::FixedRange:
mCurrentRangeLabel->setText( tr( "Range: %1 to %2" ).arg(
range.begin().toString( "yyyy-MM-dd HH:mm:ss" ),
range.end().toString( "yyyy-MM-dd HH:mm:ss" ) ) );
break;
case QgsTemporalNavigationObject::NavigationOff:
mCurrentRangeLabel->setText( tr( "Temporal navigation disabled" ) );
break;
}
}

QgsTemporalController *QgsTemporalControllerWidget::temporalController()
Expand Down
6 changes: 6 additions & 0 deletions src/gui/qgstemporalcontrollerwidget.h
Expand Up @@ -22,6 +22,7 @@

#include "qgis_gui.h"
#include "qgsrange.h"
#include "qgstemporalnavigationobject.h"

class QgsMapLayer;
class QgsTemporalNavigationObject;
Expand Down Expand Up @@ -121,6 +122,11 @@ class GUI_EXPORT QgsTemporalControllerWidget : public QgsPanelWidget, private Ui

void setWidgetStateFromProject();

void mNavigationOff_clicked();
void mNavigationFixedRange_clicked();
void mNavigationAnimated_clicked();
void setWidgetStateFromNavigationMode( const QgsTemporalNavigationObject::NavigationMode mode );

void onLayersAdded( const QList<QgsMapLayer *> &layers );
void onProjectCleared();

Expand Down

0 comments on commit ba5b416

Please sign in to comment.