Skip to content

Commit

Permalink
[temporal] Fix incorrect frame duration in last frame in animation mode
Browse files Browse the repository at this point in the history
The final frame must always have the same duration as other frames,
even if this means we go past the end of the animation range. Also
avoid showing a "zero length" frame at the end of the animation
if the frame duration fits exactly into the overall animation
range.

Fixes qgis#40777
  • Loading branch information
nyalldawson committed Jun 10, 2021
1 parent 8f0a8dd commit 0221122
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 19 deletions.
7 changes: 2 additions & 5 deletions src/core/qgstemporalnavigationobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,7 @@ QgsDateTimeRange QgsTemporalNavigationObject::dateTimeRangeForFrameNumber( long
if ( mCumulativeTemporalRange )
frameStart = start;

if ( end <= mTemporalExtents.end() )
return QgsDateTimeRange( frameStart, end, true, false );

return QgsDateTimeRange( frameStart, mTemporalExtents.end(), true, false );
return QgsDateTimeRange( frameStart, end, true, false );
}

void QgsTemporalNavigationObject::setNavigationMode( const NavigationMode mode )
Expand Down Expand Up @@ -322,7 +319,7 @@ long long QgsTemporalNavigationObject::totalFrameCount() const
else
{
QgsInterval totalAnimationLength = mTemporalExtents.end() - mTemporalExtents.begin();
return std::floor( totalAnimationLength.seconds() / mFrameDuration.seconds() ) + 1;
return static_cast< long long >( std::ceil( totalAnimationLength.seconds() / mFrameDuration.seconds() ) );
}
}

Expand Down
122 changes: 108 additions & 14 deletions tests/src/core/testqgstemporalnavigationobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@
//qgis includes...
#include <qgstemporalnavigationobject.h>

char *toString( const QgsDateTimeRange &range )
{
QString str = QStringLiteral( "<QgsDateTimeRange: %1%2, %3%4>" ).arg(
range.includeBeginning() ? QStringLiteral( "[" ) : QStringLiteral( "(" ),
range.begin().toString( Qt::ISODateWithMs ),
range.end().toString( Qt::ISODateWithMs ),
range.includeEnd() ? QStringLiteral( "]" ) : QStringLiteral( ")" ) );
char *dst = new char[str.size() + 1];
return qstrcpy( dst, str.toLocal8Bit().constData() );
}

/**
* \ingroup UnitTests
* This is a unit test for the QgsTemporalNavigationObject class.
Expand Down Expand Up @@ -189,14 +200,22 @@ void TestQgsTemporalNavigationObject::frameSettings()
true,
false
);
QgsDateTimeRange lastRange = QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 0, 0 ) ),
true,
false
);
navigationObject->setTemporalExtents( range );
QCOMPARE( temporalRangeSignal.count(), 1 );
// two frames - 8-10am, 10-12am
QCOMPARE( navigationObject->totalFrameCount(), 2LL );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 0 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 10, 0, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 1 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 10, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 0, 0 ) ),
true,
false
) );

navigationObject->setFrameDuration( QgsInterval( 1, QgsUnitTypes::TemporalHours ) );
QCOMPARE( navigationObject->frameDuration(), QgsInterval( 1, QgsUnitTypes::TemporalHours ) );
Expand All @@ -206,7 +225,32 @@ void TestQgsTemporalNavigationObject::frameSettings()
QCOMPARE( navigationObject->frameDuration().originalUnit(), QgsUnitTypes::TemporalHours );

QCOMPARE( navigationObject->currentFrameNumber(), 0 );
QCOMPARE( navigationObject->totalFrameCount(), 5 );
// four frames - 8-9, 9-10, 10-11, 11-12am
QCOMPARE( navigationObject->totalFrameCount(), 4LL );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 0 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 9, 0, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 1 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 9, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 10, 0, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 2 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 10, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 11, 0, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 3 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 11, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 0, 0 ) ),
true,
false
) );

navigationObject->setCurrentFrameNumber( 1 );
QCOMPARE( navigationObject->currentFrameNumber(), 1 );
Expand All @@ -225,19 +269,69 @@ void TestQgsTemporalNavigationObject::frameSettings()
navigationObject->setFramesPerSecond( 1 );
QCOMPARE( navigationObject->framesPerSecond(), 1.0 );

QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 4 ), lastRange );

// Test if changing the frame duration 'keeps' the current frameNumber
navigationObject->setCurrentFrameNumber( 4 ); // 12:00-...
QCOMPARE( navigationObject->currentFrameNumber(), 4 );
navigationObject->setCurrentFrameNumber( 2 ); // 10:00-11
QCOMPARE( navigationObject->currentFrameNumber(), 2 );
navigationObject->setFrameDuration( QgsInterval( 2, QgsUnitTypes::TemporalHours ) );
QCOMPARE( navigationObject->currentFrameNumber(), 2 ); // going from 1 hour to 2 hour frames, but stay on 12:00-...
QCOMPARE( navigationObject->currentFrameNumber(), 1 ); // going from 1 hour to 2 hour frames, but stay on 10:00-...
QCOMPARE( temporalRangeSignal.count(), 7 );

// Test if, when changing to Cumulative mode, the dateTimeRange for frame 4 (with 2 hours frames) is indeed the full range
// two frames - 8-10, 10-12am
QCOMPARE( navigationObject->totalFrameCount(), 2LL );

// Test if, when changing to Cumulative mode, the dateTimeRange for frame 2 (with 2 hours frames) is indeed the full range
navigationObject->setTemporalRangeCumulative( true );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 4 ), range );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 1 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 0, 0 ) ),
true,
false
) );
QCOMPARE( temporalRangeSignal.count(), 7 );

navigationObject->setTemporalRangeCumulative( false );
// interval which doesn't fit exactly into overall range
navigationObject->setFrameDuration( QgsInterval( 0.75, QgsUnitTypes::TemporalHours ) );
// six frames - 8-8.45, 8.45-9.30, 9.30-10.15, 10.15-11.00, 11.00-11.45, 11.45-12.30
QCOMPARE( navigationObject->totalFrameCount(), 6LL );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 0 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 45, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 1 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 8, 45, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 9, 30, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 2 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 9, 30, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 10, 15, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 3 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 10, 15, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 11, 0, 0 ) ),
true,
false
) );
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 4 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 11, 0, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 11, 45, 0 ) ),
true,
false
) );
// yes, this frame goes PAST the end of the overall animation range -- but we need to ensure that
// every frame has equal length!
QCOMPARE( navigationObject->dateTimeRangeForFrameNumber( 5 ), QgsDateTimeRange(
QDateTime( QDate( 2020, 1, 1 ), QTime( 11, 45, 0 ) ),
QDateTime( QDate( 2020, 1, 1 ), QTime( 12, 30, 0 ) ),
true,
false
) );
}

void TestQgsTemporalNavigationObject::expressionContext()
Expand Down

0 comments on commit 0221122

Please sign in to comment.