LCOV - code coverage report
Current view: top level - core - qgstemporalnavigationobject.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 216 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                          qgstemporalnavigationobject.cpp
       3                 :            :                          ---------------
       4                 :            :     begin                : March 2020
       5                 :            :     copyright            : (C) 2020 by Samweli Mwakisambwe
       6                 :            :     email                : samweli at kartoza dot com
       7                 :            :  ***************************************************************************/
       8                 :            : 
       9                 :            : /***************************************************************************
      10                 :            :  *                                                                         *
      11                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      12                 :            :  *   it under the terms of the GNU General Public License as published by  *
      13                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      14                 :            :  *   (at your option) any later version.                                   *
      15                 :            :  *                                                                         *
      16                 :            :  ***************************************************************************/
      17                 :            : 
      18                 :            : #include "qgstemporalnavigationobject.h"
      19                 :            : #include "qgis.h"
      20                 :            : #include "qgstemporalutils.h"
      21                 :            : 
      22                 :          0 : QgsTemporalNavigationObject::QgsTemporalNavigationObject( QObject *parent )
      23                 :          0 :   : QgsTemporalController( parent )
      24                 :          0 : {
      25                 :          0 :   mNewFrameTimer = new QTimer( this );
      26                 :            : 
      27                 :          0 :   connect( mNewFrameTimer, &QTimer::timeout,
      28                 :            :            this, &QgsTemporalNavigationObject::timerTimeout );
      29                 :          0 : }
      30                 :            : 
      31                 :          0 : void QgsTemporalNavigationObject::timerTimeout()
      32                 :            : {
      33                 :          0 :   switch ( mPlayBackMode )
      34                 :            :   {
      35                 :            :     case AnimationState::Forward:
      36                 :          0 :       next();
      37                 :          0 :       if ( mCurrentFrameNumber >= totalFrameCount() - 1 )
      38                 :            :       {
      39                 :          0 :         if ( mLoopAnimation )
      40                 :          0 :           mCurrentFrameNumber = -1; // we don't jump immediately to frame 0, instead we delay that till the next timeout
      41                 :            :         else
      42                 :          0 :           pause();
      43                 :          0 :       }
      44                 :          0 :       break;
      45                 :            : 
      46                 :            :     case AnimationState::Reverse:
      47                 :          0 :       previous();
      48                 :          0 :       if ( mCurrentFrameNumber <= 0 )
      49                 :            :       {
      50                 :          0 :         if ( mLoopAnimation )
      51                 :          0 :           mCurrentFrameNumber = totalFrameCount(); // we don't jump immediately to real last frame..., instead we delay that till the next timeout
      52                 :            :         else
      53                 :          0 :           pause();
      54                 :          0 :       }
      55                 :          0 :       break;
      56                 :            : 
      57                 :            :     case AnimationState::Idle:
      58                 :            :       // should not happen - in an idle state the timeout won't occur
      59                 :          0 :       break;
      60                 :            :   }
      61                 :          0 : }
      62                 :            : 
      63                 :          0 : bool QgsTemporalNavigationObject::isLooping() const
      64                 :            : {
      65                 :          0 :   return mLoopAnimation;
      66                 :            : }
      67                 :            : 
      68                 :          0 : void QgsTemporalNavigationObject::setLooping( bool loopAnimation )
      69                 :            : {
      70                 :          0 :   mLoopAnimation = loopAnimation;
      71                 :          0 : }
      72                 :            : 
      73                 :          0 : QgsExpressionContextScope *QgsTemporalNavigationObject::createExpressionContextScope() const
      74                 :            : {
      75                 :          0 :   std::unique_ptr< QgsExpressionContextScope > scope = std::make_unique< QgsExpressionContextScope >( QStringLiteral( "temporal" ) );
      76                 :          0 :   scope->setVariable( QStringLiteral( "frame_rate" ), mFramesPerSecond, true );
      77                 :          0 :   scope->setVariable( QStringLiteral( "frame_number" ), mCurrentFrameNumber, true );
      78                 :          0 :   scope->setVariable( QStringLiteral( "frame_duration" ), mFrameDuration, true );
      79                 :          0 :   scope->setVariable( QStringLiteral( "frame_timestep" ), mFrameDuration.originalDuration(), true );
      80                 :          0 :   scope->setVariable( QStringLiteral( "frame_timestep_unit" ), mFrameDuration.originalUnit(), true );
      81                 :          0 :   scope->setVariable( QStringLiteral( "animation_start_time" ), mTemporalExtents.begin(), true );
      82                 :          0 :   scope->setVariable( QStringLiteral( "animation_end_time" ), mTemporalExtents.end(), true );
      83                 :          0 :   scope->setVariable( QStringLiteral( "animation_interval" ), mTemporalExtents.end() - mTemporalExtents.begin(), true );
      84                 :          0 :   return scope.release();
      85                 :          0 : }
      86                 :            : 
      87                 :          0 : QgsDateTimeRange QgsTemporalNavigationObject::dateTimeRangeForFrameNumber( long long frame ) const
      88                 :            : {
      89                 :          0 :   const QDateTime start = mTemporalExtents.begin();
      90                 :            : 
      91                 :          0 :   if ( frame < 0 )
      92                 :          0 :     frame = 0;
      93                 :            : 
      94                 :          0 :   const long long nextFrame = frame + 1;
      95                 :            : 
      96                 :          0 :   QDateTime begin;
      97                 :          0 :   QDateTime end;
      98                 :          0 :   if ( mFrameDuration.originalUnit() == QgsUnitTypes::TemporalIrregularStep )
      99                 :            :   {
     100                 :          0 :     if ( mAllRanges.empty() )
     101                 :          0 :       return QgsDateTimeRange();
     102                 :            : 
     103                 :          0 :     return frame < mAllRanges.size() ? mAllRanges.at( frame ) : mAllRanges.constLast();
     104                 :            :   }
     105                 :            :   else
     106                 :            :   {
     107                 :          0 :     begin = QgsTemporalUtils::calculateFrameTime( start, frame, mFrameDuration );
     108                 :          0 :     end = QgsTemporalUtils::calculateFrameTime( start, nextFrame, mFrameDuration );
     109                 :            :   }
     110                 :            : 
     111                 :          0 :   QDateTime frameStart = begin;
     112                 :            : 
     113                 :          0 :   if ( mCumulativeTemporalRange )
     114                 :          0 :     frameStart = start;
     115                 :            : 
     116                 :          0 :   if ( end <= mTemporalExtents.end() )
     117                 :          0 :     return QgsDateTimeRange( frameStart, end, true, false );
     118                 :            : 
     119                 :          0 :   return QgsDateTimeRange( frameStart, mTemporalExtents.end(), true, false );
     120                 :          0 : }
     121                 :            : 
     122                 :          0 : void QgsTemporalNavigationObject::setNavigationMode( const NavigationMode mode )
     123                 :            : {
     124                 :          0 :   if ( mNavigationMode == mode )
     125                 :          0 :     return;
     126                 :            : 
     127                 :          0 :   mNavigationMode = mode;
     128                 :          0 :   emit navigationModeChanged( mode );
     129                 :            : 
     130                 :          0 :   if ( !mBlockUpdateTemporalRangeSignal )
     131                 :            :   {
     132                 :          0 :     switch ( mNavigationMode )
     133                 :            :     {
     134                 :            :       case Animated:
     135                 :          0 :         emit updateTemporalRange( dateTimeRangeForFrameNumber( mCurrentFrameNumber ) );
     136                 :          0 :         break;
     137                 :            :       case FixedRange:
     138                 :          0 :         emit updateTemporalRange( mTemporalExtents );
     139                 :          0 :         break;
     140                 :            :       case NavigationOff:
     141                 :          0 :         emit updateTemporalRange( QgsDateTimeRange() );
     142                 :          0 :         break;
     143                 :            :     }
     144                 :          0 :   }
     145                 :          0 : }
     146                 :            : 
     147                 :          0 : void QgsTemporalNavigationObject::setTemporalExtents( const QgsDateTimeRange &temporalExtents )
     148                 :            : {
     149                 :          0 :   if ( mTemporalExtents == temporalExtents )
     150                 :            :   {
     151                 :          0 :     return;
     152                 :            :   }
     153                 :          0 :   QgsDateTimeRange oldFrame = dateTimeRangeForFrameNumber( currentFrameNumber() );
     154                 :          0 :   mTemporalExtents = temporalExtents;
     155                 :          0 :   mCurrentFrameNumber = findBestFrameNumberForFrameStart( oldFrame.begin() );
     156                 :          0 :   emit temporalExtentsChanged( mTemporalExtents );
     157                 :            : 
     158                 :          0 :   switch ( mNavigationMode )
     159                 :            :   {
     160                 :            :     case Animated:
     161                 :            :     {
     162                 :          0 :       int currentFrameNumber = mCurrentFrameNumber;
     163                 :            : 
     164                 :            :       // Force to emit signal if the current frame number doesn't change
     165                 :          0 :       if ( currentFrameNumber == mCurrentFrameNumber && !mBlockUpdateTemporalRangeSignal )
     166                 :          0 :         emit updateTemporalRange( dateTimeRangeForFrameNumber( mCurrentFrameNumber ) );
     167                 :          0 :       break;
     168                 :            :     }
     169                 :            :     case FixedRange:
     170                 :          0 :       if ( !mBlockUpdateTemporalRangeSignal )
     171                 :          0 :         emit updateTemporalRange( mTemporalExtents );
     172                 :          0 :       break;
     173                 :            :     case NavigationOff:
     174                 :          0 :       break;
     175                 :            :   }
     176                 :            : 
     177                 :          0 : }
     178                 :            : 
     179                 :          0 : QgsDateTimeRange QgsTemporalNavigationObject::temporalExtents() const
     180                 :            : {
     181                 :          0 :   return mTemporalExtents;
     182                 :            : }
     183                 :            : 
     184                 :          0 : void QgsTemporalNavigationObject::setAvailableTemporalRanges( const QList<QgsDateTimeRange> &ranges )
     185                 :            : {
     186                 :          0 :   mAllRanges = ranges;
     187                 :          0 : }
     188                 :            : 
     189                 :          0 : QList<QgsDateTimeRange> QgsTemporalNavigationObject::availableTemporalRanges() const
     190                 :            : {
     191                 :          0 :   return mAllRanges;
     192                 :            : }
     193                 :            : 
     194                 :          0 : void QgsTemporalNavigationObject::setCurrentFrameNumber( long long frameNumber )
     195                 :            : {
     196                 :          0 :   if ( mCurrentFrameNumber != frameNumber )
     197                 :            :   {
     198                 :          0 :     mCurrentFrameNumber = std::max( 0LL, std::min( frameNumber, totalFrameCount() - 1 ) );
     199                 :          0 :     QgsDateTimeRange range = dateTimeRangeForFrameNumber( mCurrentFrameNumber );
     200                 :            : 
     201                 :          0 :     if ( !mBlockUpdateTemporalRangeSignal )
     202                 :          0 :       emit updateTemporalRange( range );
     203                 :          0 :   }
     204                 :          0 : }
     205                 :            : 
     206                 :          0 : long long QgsTemporalNavigationObject::currentFrameNumber() const
     207                 :            : {
     208                 :          0 :   return mCurrentFrameNumber;
     209                 :            : }
     210                 :            : 
     211                 :          0 : void QgsTemporalNavigationObject::setFrameDuration( const QgsInterval &frameDuration )
     212                 :            : {
     213                 :          0 :   if ( mFrameDuration == frameDuration )
     214                 :            :   {
     215                 :          0 :     return;
     216                 :            :   }
     217                 :            : 
     218                 :          0 :   QgsDateTimeRange oldFrame = dateTimeRangeForFrameNumber( currentFrameNumber() );
     219                 :          0 :   mFrameDuration = frameDuration;
     220                 :            : 
     221                 :          0 :   mCurrentFrameNumber = findBestFrameNumberForFrameStart( oldFrame.begin() );
     222                 :          0 :   emit temporalFrameDurationChanged( mFrameDuration );
     223                 :            : 
     224                 :            :   // forcing an update of our views
     225                 :          0 :   QgsDateTimeRange range = dateTimeRangeForFrameNumber( mCurrentFrameNumber );
     226                 :            : 
     227                 :          0 :   if ( !mBlockUpdateTemporalRangeSignal && mNavigationMode == Animated )
     228                 :          0 :     emit updateTemporalRange( range );
     229                 :          0 : }
     230                 :            : 
     231                 :          0 : QgsInterval QgsTemporalNavigationObject::frameDuration() const
     232                 :            : {
     233                 :          0 :   return mFrameDuration;
     234                 :            : }
     235                 :            : 
     236                 :          0 : void QgsTemporalNavigationObject::setFramesPerSecond( double framesPerSeconds )
     237                 :            : {
     238                 :          0 :   if ( framesPerSeconds > 0 )
     239                 :            :   {
     240                 :          0 :     mFramesPerSecond = framesPerSeconds;
     241                 :          0 :     mNewFrameTimer->setInterval( ( 1.0 / mFramesPerSecond ) * 1000 );
     242                 :          0 :   }
     243                 :          0 : }
     244                 :            : 
     245                 :          0 : double QgsTemporalNavigationObject::framesPerSecond() const
     246                 :            : {
     247                 :          0 :   return mFramesPerSecond;
     248                 :            : }
     249                 :            : 
     250                 :          0 : void QgsTemporalNavigationObject::setTemporalRangeCumulative( bool state )
     251                 :            : {
     252                 :          0 :   mCumulativeTemporalRange = state;
     253                 :          0 : }
     254                 :            : 
     255                 :          0 : bool QgsTemporalNavigationObject::temporalRangeCumulative() const
     256                 :            : {
     257                 :          0 :   return mCumulativeTemporalRange;
     258                 :            : }
     259                 :            : 
     260                 :          0 : void QgsTemporalNavigationObject::play()
     261                 :            : {
     262                 :          0 :   mNewFrameTimer->start( ( 1.0 / mFramesPerSecond ) * 1000 );
     263                 :          0 : }
     264                 :            : 
     265                 :          0 : void QgsTemporalNavigationObject::pause()
     266                 :            : {
     267                 :          0 :   mNewFrameTimer->stop();
     268                 :          0 :   setAnimationState( AnimationState::Idle );
     269                 :          0 : }
     270                 :            : 
     271                 :          0 : void QgsTemporalNavigationObject::playForward()
     272                 :            : {
     273                 :          0 :   if ( mPlayBackMode == Idle &&  mCurrentFrameNumber >= totalFrameCount() - 1 )
     274                 :            :   {
     275                 :            :     // if we are paused at the end of the video, and the user hits play, we automatically rewind and play again
     276                 :          0 :     rewindToStart();
     277                 :          0 :   }
     278                 :            : 
     279                 :          0 :   setAnimationState( AnimationState::Forward );
     280                 :          0 :   play();
     281                 :          0 : }
     282                 :            : 
     283                 :          0 : void QgsTemporalNavigationObject::playBackward()
     284                 :            : {
     285                 :          0 :   if ( mPlayBackMode == Idle &&  mCurrentFrameNumber <= 0 )
     286                 :            :   {
     287                 :            :     // if we are paused at the start of the video, and the user hits play, we automatically skip to end and play in reverse again
     288                 :          0 :     skipToEnd();
     289                 :          0 :   }
     290                 :            : 
     291                 :          0 :   setAnimationState( AnimationState::Reverse );
     292                 :          0 :   play();
     293                 :          0 : }
     294                 :            : 
     295                 :          0 : void QgsTemporalNavigationObject::next()
     296                 :            : {
     297                 :          0 :   setCurrentFrameNumber( mCurrentFrameNumber + 1 );
     298                 :          0 : }
     299                 :            : 
     300                 :          0 : void QgsTemporalNavigationObject::previous()
     301                 :            : {
     302                 :          0 :   setCurrentFrameNumber( mCurrentFrameNumber - 1 );
     303                 :          0 : }
     304                 :            : 
     305                 :          0 : void QgsTemporalNavigationObject::rewindToStart()
     306                 :            : {
     307                 :          0 :   setCurrentFrameNumber( 0 );
     308                 :          0 : }
     309                 :            : 
     310                 :          0 : void QgsTemporalNavigationObject::skipToEnd()
     311                 :            : {
     312                 :          0 :   const long long frame = totalFrameCount() - 1;
     313                 :          0 :   setCurrentFrameNumber( frame );
     314                 :          0 : }
     315                 :            : 
     316                 :          0 : long long QgsTemporalNavigationObject::totalFrameCount() const
     317                 :            : {
     318                 :          0 :   if ( mFrameDuration.originalUnit() == QgsUnitTypes::TemporalIrregularStep )
     319                 :            :   {
     320                 :          0 :     return mAllRanges.count();
     321                 :            :   }
     322                 :            :   else
     323                 :            :   {
     324                 :          0 :     QgsInterval totalAnimationLength = mTemporalExtents.end() - mTemporalExtents.begin();
     325                 :          0 :     return std::floor( totalAnimationLength.seconds() / mFrameDuration.seconds() ) + 1;
     326                 :          0 :   }
     327                 :          0 : }
     328                 :            : 
     329                 :          0 : void QgsTemporalNavigationObject::setAnimationState( AnimationState mode )
     330                 :            : {
     331                 :          0 :   if ( mode != mPlayBackMode )
     332                 :          0 :   {
     333                 :          0 :     mPlayBackMode = mode;
     334                 :          0 :     emit stateChanged( mPlayBackMode );
     335                 :          0 :   }
     336                 :          0 : }
     337                 :            : 
     338                 :          0 : QgsTemporalNavigationObject::AnimationState QgsTemporalNavigationObject::animationState() const
     339                 :            : {
     340                 :          0 :   return mPlayBackMode;
     341                 :          0 : }
     342                 :            : 
     343                 :          0 : long long QgsTemporalNavigationObject::findBestFrameNumberForFrameStart( const QDateTime &frameStart ) const
     344                 :            : {
     345                 :          0 :   long long bestFrame = 0;
     346                 :          0 :   if ( mFrameDuration.originalUnit() == QgsUnitTypes::TemporalIrregularStep )
     347                 :          0 :   {
     348                 :          0 :     for ( const QgsDateTimeRange &range : mAllRanges )
     349                 :            :     {
     350                 :          0 :       if ( range.contains( frameStart ) )
     351                 :          0 :         return bestFrame;
     352                 :          0 :       else if ( range.begin() > frameStart )
     353                 :            :         // if we've gone past the target date, go back one frame if possible
     354                 :          0 :         return std::max( 0LL, bestFrame - 1 );
     355                 :          0 :       bestFrame++;
     356                 :            :     }
     357                 :          0 :     return mAllRanges.count() - 1;
     358                 :            :   }
     359                 :            :   else
     360                 :            :   {
     361                 :          0 :     QgsDateTimeRange testFrame = QgsDateTimeRange( frameStart, frameStart ); // creating an 'instant' Range
     362                 :            :     // Earlier we looped from frame 0 till totalFrameCount() here, but this loop grew potentially gigantic
     363                 :          0 :     long long roughFrameStart = 0;
     364                 :          0 :     long long roughFrameEnd = totalFrameCount();
     365                 :            :     // For the smaller step frames we calculate an educated guess, to prevent the loop becoming too
     366                 :            :     // large, freezing the ui (eg having a mTemporalExtents of several months and the user selects milliseconds)
     367                 :          0 :     if ( mFrameDuration.originalUnit() != QgsUnitTypes::TemporalMonths && mFrameDuration.originalUnit() != QgsUnitTypes::TemporalYears && mFrameDuration.originalUnit() != QgsUnitTypes::TemporalDecades && mFrameDuration.originalUnit() != QgsUnitTypes::TemporalCenturies )
     368                 :            :     {
     369                 :            :       // Only if we receive a valid frameStart, that is within current mTemporalExtents
     370                 :            :       // We tend to receive a framestart of 'now()' upon startup for example
     371                 :          0 :       if ( mTemporalExtents.contains( frameStart ) )
     372                 :            :       {
     373                 :          0 :         roughFrameStart = std::floor( ( frameStart - mTemporalExtents.begin() ).seconds() / mFrameDuration.seconds() );
     374                 :          0 :       }
     375                 :          0 :       roughFrameEnd = roughFrameStart + 100; // just in case we miss the guess
     376                 :          0 :     }
     377                 :          0 :     for ( long long i = roughFrameStart; i < roughFrameEnd; ++i )
     378                 :            :     {
     379                 :          0 :       QgsDateTimeRange range = dateTimeRangeForFrameNumber( i );
     380                 :          0 :       if ( range.overlaps( testFrame ) )
     381                 :            :       {
     382                 :          0 :         bestFrame = i;
     383                 :          0 :         break;
     384                 :            :       }
     385                 :          0 :     }
     386                 :          0 :     return bestFrame;
     387                 :          0 :   }
     388                 :          0 : }

Generated by: LCOV version 1.14