Skip to content

Commit

Permalink
[temporal] Fix broken animations when a non-integer interval value
Browse files Browse the repository at this point in the history
is used (e.g. 1.5 hours)

The frame number was not being considered, making it impossible
to advance the animation
  • Loading branch information
nyalldawson committed Jun 3, 2021
1 parent 191e9f9 commit 81ec79c
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 17 deletions.
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgstemporalutils.sip.in
Expand Up @@ -79,7 +79,7 @@ support.
- error: will be set to a descriptive error message if the export fails
%End

static QDateTime calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval interval );
static QDateTime calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval &interval );
%Docstring
Calculates the frame time for an animation.

Expand Down
24 changes: 10 additions & 14 deletions src/core/qgstemporalutils.cpp
Expand Up @@ -162,54 +162,50 @@ bool QgsTemporalUtils::exportAnimation( const QgsMapSettings &mapSettings, const
}


QDateTime QgsTemporalUtils::calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval interval )
QDateTime QgsTemporalUtils::calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval &interval )
{

double unused;
const bool isFractional = !qgsDoubleNear( fabs( modf( interval.originalDuration(), &unused ) ), 0.0 );

if ( isFractional || interval.originalUnit() == QgsUnitTypes::TemporalUnit::TemporalUnknownUnit )
{
return start + interval;
const double duration = interval.seconds();
return start.addMSecs( frame * duration * 1000 );
}
else
{
switch ( interval.originalUnit() )
{
case QgsUnitTypes::TemporalUnit::TemporalMilliseconds:
return start.addMSecs( frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalSeconds:
return start.addSecs( frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalMinutes:
return start.addSecs( 60 * frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalHours:
return start.addSecs( 3600 * frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalDays:
return start.addDays( frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalWeeks:
return start.addDays( 7 * frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalMonths:
return start.addMonths( frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalYears:
return start.addYears( frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalDecades:
return start.addYears( 10 * frame * interval.originalDuration() );
break;
case QgsUnitTypes::TemporalUnit::TemporalCenturies:
return start.addYears( 100 * frame * interval.originalDuration() );
break;
default:
return start;
case QgsUnitTypes::TemporalUnit::TemporalUnknownUnit:
// handled above
return QDateTime();
case QgsUnitTypes::TemporalUnit::TemporalIrregularStep:
// not supported by this method
return QDateTime();
}
}
return QDateTime();
}

QList<QDateTime> QgsTemporalUtils::calculateDateTimesUsingDuration( const QDateTime &start, const QDateTime &end, const QString &duration, bool &ok, bool &maxValuesExceeded, int maxValues )
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgstemporalutils.h
Expand Up @@ -208,7 +208,7 @@ class CORE_EXPORT QgsTemporalUtils
*
* \since QGIS 3.18
*/
static QDateTime calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval interval );
static QDateTime calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval &interval );

/**
* Calculates a complete list of datetimes between \a start and \a end, using the specified ISO8601 \a duration string (eg "PT12H").
Expand Down
38 changes: 37 additions & 1 deletion tests/src/python/test_qgstemporalutils.py
Expand Up @@ -120,12 +120,30 @@ def testFrameTimeCalculation(self):
}

for unit in list(expected2.keys()):

f = QgsTemporalUtils.calculateFrameTime(QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 0), Qt.UTC),
1,
QgsInterval(10.5, unit))
self.assertEqual(f, expected2[unit])

# frame number > 1
expected2a = {QgsUnitTypes.TemporalMilliseconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 31), Qt.UTC),
QgsUnitTypes.TemporalSeconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 31, 500), Qt.UTC),
QgsUnitTypes.TemporalMinutes: QDateTime(QDate(2021, 1, 1), QTime(12, 31, 30, 0), Qt.UTC),
QgsUnitTypes.TemporalHours: QDateTime(QDate(2021, 1, 2), QTime(19, 30, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalDays: QDateTime(QDate(2021, 2, 2), QTime(0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalWeeks: QDateTime(QDate(2021, 8, 10), QTime(0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalMonths: QDateTime(QDate(2023, 8, 4), QTime(12, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalYears: QDateTime(QDate(2052, 7, 2), QTime(21, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalDecades: QDateTime(QDate(2336, 1, 5), QTime(6, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalCenturies: QDateTime(QDate(5171, 1, 26), QTime(0, 0, 0, 0), Qt.UTC)
}

for unit in list(expected2.keys()):
f = QgsTemporalUtils.calculateFrameTime(QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 0), Qt.UTC),
3,
QgsInterval(10.5, unit))
self.assertEqual(f, expected2a[unit])

expected3 = {QgsUnitTypes.TemporalMilliseconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalSeconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 200), Qt.UTC),
QgsUnitTypes.TemporalMinutes: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 12, 0), Qt.UTC),
Expand All @@ -144,6 +162,24 @@ def testFrameTimeCalculation(self):
QgsInterval(0.2, unit))
self.assertEqual(f, expected3[unit])

expected3a = {QgsUnitTypes.TemporalMilliseconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalSeconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 600), Qt.UTC),
QgsUnitTypes.TemporalMinutes: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 36, 0), Qt.UTC),
QgsUnitTypes.TemporalHours: QDateTime(QDate(2021, 1, 1), QTime(12, 36, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalDays: QDateTime(QDate(2021, 1, 2), QTime(2, 24, 0), Qt.UTC),
QgsUnitTypes.TemporalWeeks: QDateTime(QDate(2021, 1, 5), QTime(16, 48, 0), Qt.UTC),
QgsUnitTypes.TemporalMonths: QDateTime(QDate(2021, 1, 19), QTime(12, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalYears: QDateTime(QDate(2021, 8, 8), QTime(15, 36, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalDecades: QDateTime(QDate(2027, 1, 2), QTime(0, 0, 0, 0), Qt.UTC),
QgsUnitTypes.TemporalCenturies: QDateTime(QDate(2081, 1, 1), QTime(12, 0, 0, 0), Qt.UTC)
}

for unit in list(expected3.keys()):
f = QgsTemporalUtils.calculateFrameTime(QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 0), Qt.UTC),
3,
QgsInterval(0.2, unit))
self.assertEqual(f, expected3a[unit])

def testCalculateDateTimesUsingDuration(self):
# invalid duration string
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
Expand Down

0 comments on commit 81ec79c

Please sign in to comment.