QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgstemporalcontrollerwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstemporalcontrollerwidget.cpp
3  ------------------------------
4  begin : February 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 "qgsapplication.h"
20 #include "qgsgui.h"
21 #include "qgsmaplayermodel.h"
22 #include "qgsproject.h"
23 #include "qgsprojecttimesettings.h"
25 #include "qgstemporalutils.h"
27 #include "qgsmeshlayer.h"
28 
29 #include <QAction>
30 #include <QMenu>
31 
33  : QgsPanelWidget( parent )
34 {
35  setupUi( this );
36 
37  mNavigationObject = new QgsTemporalNavigationObject( this );
38 
39  mStartDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
40  mEndDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
41  mFixedRangeStartDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
42  mFixedRangeEndDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
43 
44  connect( mForwardButton, &QPushButton::clicked, this, &QgsTemporalControllerWidget::togglePlayForward );
45  connect( mBackButton, &QPushButton::clicked, this, &QgsTemporalControllerWidget::togglePlayBackward );
46  connect( mStopButton, &QPushButton::clicked, this, &QgsTemporalControllerWidget::togglePause );
47  connect( mNextButton, &QPushButton::clicked, mNavigationObject, &QgsTemporalNavigationObject::next );
48  connect( mPreviousButton, &QPushButton::clicked, mNavigationObject, &QgsTemporalNavigationObject::previous );
49  connect( mFastForwardButton, &QPushButton::clicked, mNavigationObject, &QgsTemporalNavigationObject::skipToEnd );
50  connect( mRewindButton, &QPushButton::clicked, mNavigationObject, &QgsTemporalNavigationObject::rewindToStart );
51  connect( mLoopingCheckBox, &QCheckBox::toggled, this, [ = ]( bool state ) { mNavigationObject->setLooping( state ); } );
52 
53  setWidgetStateFromNavigationMode( mNavigationObject->navigationMode() );
54  connect( mNavigationObject, &QgsTemporalNavigationObject::navigationModeChanged, this, &QgsTemporalControllerWidget::setWidgetStateFromNavigationMode );
55  connect( mNavigationObject, &QgsTemporalNavigationObject::temporalExtentsChanged, this, &QgsTemporalControllerWidget::setDates );
56  connect( mNavigationObject, &QgsTemporalNavigationObject::temporalFrameDurationChanged, this, [ = ]( const QgsInterval & timeStep )
57  {
58  if ( mBlockFrameDurationUpdates )
59  return;
60 
61  mBlockFrameDurationUpdates++;
62  setTimeStep( timeStep );
63  mBlockFrameDurationUpdates--;
64  } );
65  connect( mNavigationOff, &QPushButton::clicked, this, &QgsTemporalControllerWidget::mNavigationOff_clicked );
66  connect( mNavigationFixedRange, &QPushButton::clicked, this, &QgsTemporalControllerWidget::mNavigationFixedRange_clicked );
67  connect( mNavigationAnimated, &QPushButton::clicked, this, &QgsTemporalControllerWidget::mNavigationAnimated_clicked );
68 
69  connect( mNavigationObject, &QgsTemporalNavigationObject::stateChanged, this, [ = ]( QgsTemporalNavigationObject::AnimationState state )
70  {
71  mForwardButton->setChecked( state == QgsTemporalNavigationObject::Forward );
72  mBackButton->setChecked( state == QgsTemporalNavigationObject::Reverse );
73  mStopButton->setChecked( state == QgsTemporalNavigationObject::Idle );
74  } );
75 
76  connect( mStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsTemporalControllerWidget::startEndDateTime_changed );
77  connect( mEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsTemporalControllerWidget::startEndDateTime_changed );
78  connect( mFixedRangeStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsTemporalControllerWidget::fixedRangeStartEndDateTime_changed );
79  connect( mFixedRangeEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsTemporalControllerWidget::fixedRangeStartEndDateTime_changed );
80  connect( mStepSpinBox, qgis::overload<double>::of( &QDoubleSpinBox::valueChanged ), this, &QgsTemporalControllerWidget::updateFrameDuration );
81  connect( mTimeStepsComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsTemporalControllerWidget::updateFrameDuration );
82  connect( mSlider, &QSlider::valueChanged, this, &QgsTemporalControllerWidget::timeSlider_valueChanged );
83 
84  mStepSpinBox->setClearValue( 1 );
85 
86  connect( mNavigationObject, &QgsTemporalNavigationObject::updateTemporalRange, this, &QgsTemporalControllerWidget::updateSlider );
87 
88  connect( mSettings, &QPushButton::clicked, this, &QgsTemporalControllerWidget::settings_clicked );
89 
90  mMapLayerModel = new QgsMapLayerModel( this );
91 
92  mRangeMenu.reset( new QMenu( this ) );
93 
94  mRangeSetToAllLayersAction = new QAction( tr( "Set to Full Range" ), mRangeMenu.get() );
95  mRangeSetToAllLayersAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRefresh.svg" ) ) );
96  connect( mRangeSetToAllLayersAction, &QAction::triggered, this, &QgsTemporalControllerWidget::mRangeSetToAllLayersAction_triggered );
97  mRangeMenu->addAction( mRangeSetToAllLayersAction );
98 
99  mRangeSetToProjectAction = new QAction( tr( "Set to Preset Project Range" ), mRangeMenu.get() );
100  connect( mRangeSetToProjectAction, &QAction::triggered, this, &QgsTemporalControllerWidget::mRangeSetToProjectAction_triggered );
101  mRangeMenu->addAction( mRangeSetToProjectAction );
102 
103  mRangeMenu->addSeparator();
104 
105  mRangeLayersSubMenu.reset( new QMenu( tr( "Set to Single Layer's Range" ), mRangeMenu.get() ) );
106  mRangeLayersSubMenu->setEnabled( false );
107  mRangeMenu->addMenu( mRangeLayersSubMenu.get() );
108  connect( mRangeMenu.get(), &QMenu::aboutToShow, this, &QgsTemporalControllerWidget::aboutToShowRangeMenu );
109 
110  mSetRangeButton->setPopupMode( QToolButton::MenuButtonPopup );
111  mSetRangeButton->setMenu( mRangeMenu.get() );
112  mSetRangeButton->setDefaultAction( mRangeSetToAllLayersAction );
113  mFixedRangeSetRangeButton->setPopupMode( QToolButton::MenuButtonPopup );
114  mFixedRangeSetRangeButton->setMenu( mRangeMenu.get() );
115  mFixedRangeSetRangeButton->setDefaultAction( mRangeSetToAllLayersAction );
116 
117  connect( mExportAnimationButton, &QPushButton::clicked, this, &QgsTemporalControllerWidget::exportAnimation );
118 
119  QgsDateTimeRange range;
120 
121  if ( QgsProject::instance()->timeSettings() )
123 
124  mStartDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
125  mEndDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
126  mFixedRangeStartDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
127  mFixedRangeEndDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
128 
129  if ( range.begin().isValid() && range.end().isValid() )
130  {
131  whileBlocking( mStartDateTime )->setDateTime( range.begin() );
132  whileBlocking( mEndDateTime )->setDateTime( range.end() );
133  whileBlocking( mFixedRangeStartDateTime )->setDateTime( range.begin() );
134  whileBlocking( mFixedRangeEndDateTime )->setDateTime( range.end() );
135  }
136 
138  {
149  } )
150  {
151  mTimeStepsComboBox->addItem( QgsUnitTypes::toString( u ), u );
152  }
153 
154  // TODO: might want to choose an appropriate default unit based on the range
155  mTimeStepsComboBox->setCurrentIndex( mTimeStepsComboBox->findData( QgsUnitTypes::TemporalHours ) );
156 
157  mStepSpinBox->setMinimum( 0.0000001 );
158  mStepSpinBox->setMaximum( std::numeric_limits<int>::max() );
159  mStepSpinBox->setSingleStep( 1 );
160  mStepSpinBox->setValue( 1 );
161 
162  mForwardButton->setToolTip( tr( "Play" ) );
163  mBackButton->setToolTip( tr( "Reverse" ) );
164  mNextButton->setToolTip( tr( "Go to next frame" ) );
165  mPreviousButton->setToolTip( tr( "Go to previous frame" ) );
166  mStopButton->setToolTip( tr( "Pause" ) );
167  mRewindButton->setToolTip( tr( "Rewind to start" ) );
168  mFastForwardButton->setToolTip( tr( "Fast forward to end" ) );
169 
170  updateFrameDuration();
171 
172  connect( QgsProject::instance(), &QgsProject::readProject, this, &QgsTemporalControllerWidget::setWidgetStateFromProject );
173  connect( QgsProject::instance(), &QgsProject::layersAdded, this, &QgsTemporalControllerWidget::onLayersAdded );
174  connect( QgsProject::instance(), &QgsProject::cleared, this, &QgsTemporalControllerWidget::onProjectCleared );
175 }
176 
178 {
179  if ( mSlider->hasFocus() && e->key() == Qt::Key_Space )
180  {
181  togglePause();
182  }
183  QWidget::keyPressEvent( e );
184 }
185 
186 void QgsTemporalControllerWidget::aboutToShowRangeMenu()
187 {
188  QgsDateTimeRange projectRange;
189  if ( QgsProject::instance()->timeSettings() )
190  projectRange = QgsProject::instance()->timeSettings()->temporalRange();
191  mRangeSetToProjectAction->setEnabled( projectRange.begin().isValid() && projectRange.end().isValid() );
192 
193  mRangeLayersSubMenu->clear();
194  for ( int i = 0; i < mMapLayerModel->rowCount(); ++i )
195  {
196  QModelIndex index = mMapLayerModel->index( i, 0 );
197  QgsMapLayer *currentLayer = mMapLayerModel->data( index, QgsMapLayerModel::LayerRole ).value<QgsMapLayer *>();
198  if ( !currentLayer->temporalProperties() || !currentLayer->temporalProperties()->isActive() )
199  continue;
200 
201  QIcon icon = qvariant_cast<QIcon>( mMapLayerModel->data( index, Qt::DecorationRole ) );
202  QString text = mMapLayerModel->data( index, Qt::DisplayRole ).toString();
203  QgsDateTimeRange range = currentLayer->temporalProperties()->calculateTemporalExtent( currentLayer );
204  if ( range.begin().isValid() && range.end().isValid() )
205  {
206  QAction *action = new QAction( icon, text, mRangeLayersSubMenu.get() );
207  connect( action, &QAction::triggered, this, [ = ]
208  {
209  setDates( range );
210  saveRangeToProject();
211  } );
212  mRangeLayersSubMenu->addAction( action );
213  }
214  }
215  mRangeLayersSubMenu->setEnabled( !mRangeLayersSubMenu->actions().isEmpty() );
216 }
217 
218 void QgsTemporalControllerWidget::togglePlayForward()
219 {
220  mPlayingForward = true;
221 
222  if ( mNavigationObject->animationState() != QgsTemporalNavigationObject::Forward )
223  {
224  mStopButton->setChecked( false );
225  mBackButton->setChecked( false );
226  mForwardButton->setChecked( true );
227  mNavigationObject->playForward();
228  }
229  else
230  {
231  mBackButton->setChecked( true );
232  mForwardButton->setChecked( false );
233  mNavigationObject->pause();
234  }
235 }
236 
237 void QgsTemporalControllerWidget::togglePlayBackward()
238 {
239  mPlayingForward = false;
240 
241  if ( mNavigationObject->animationState() != QgsTemporalNavigationObject::Reverse )
242  {
243  mStopButton->setChecked( false );
244  mBackButton->setChecked( true );
245  mForwardButton->setChecked( false );
246  mNavigationObject->playBackward();
247  }
248  else
249  {
250  mBackButton->setChecked( true );
251  mBackButton->setChecked( false );
252  mNavigationObject->pause();
253  }
254 }
255 
256 void QgsTemporalControllerWidget::togglePause()
257 {
258  if ( mNavigationObject->animationState() != QgsTemporalNavigationObject::Idle )
259  {
260  mStopButton->setChecked( true );
261  mBackButton->setChecked( false );
262  mForwardButton->setChecked( false );
263  mNavigationObject->pause();
264  }
265  else
266  {
267  mBackButton->setChecked( mPlayingForward ? false : true );
268  mForwardButton->setChecked( mPlayingForward ? false : true );
269  if ( mPlayingForward )
270  {
271  mNavigationObject->playForward();
272  }
273  else
274  {
275  mNavigationObject->playBackward();
276  }
277  }
278 }
279 
280 void QgsTemporalControllerWidget::updateTemporalExtent()
281 {
282  QgsDateTimeRange temporalExtent = QgsDateTimeRange( mStartDateTime->dateTime(),
283  mEndDateTime->dateTime() );
284  mNavigationObject->setTemporalExtents( temporalExtent );
285  mSlider->setRange( 0, mNavigationObject->totalFrameCount() - 1 );
286  mSlider->setValue( 0 );
287 }
288 
289 void QgsTemporalControllerWidget::updateFrameDuration()
290 {
291  if ( mBlockSettingUpdates )
292  return;
293 
294  // save new settings into project
295  QgsProject::instance()->timeSettings()->setTimeStepUnit( static_cast< QgsUnitTypes::TemporalUnit>( mTimeStepsComboBox->currentData().toInt() ) );
296  QgsProject::instance()->timeSettings()->setTimeStep( mStepSpinBox->value() );
297 
298  if ( !mBlockFrameDurationUpdates )
299  mNavigationObject->setFrameDuration( QgsInterval( QgsProject::instance()->timeSettings()->timeStep(),
300  QgsProject::instance()->timeSettings()->timeStepUnit() ) );
301 
302  mSlider->setRange( 0, mNavigationObject->totalFrameCount() - 1 );
303 }
304 
305 void QgsTemporalControllerWidget::setWidgetStateFromProject()
306 {
307  mBlockSettingUpdates++;
308  mTimeStepsComboBox->setCurrentIndex( mTimeStepsComboBox->findData( QgsProject::instance()->timeSettings()->timeStepUnit() ) );
309  mStepSpinBox->setValue( QgsProject::instance()->timeSettings()->timeStep() );
310  mBlockSettingUpdates--;
311 
312  bool ok = false;
313  QgsTemporalNavigationObject::NavigationMode mode = static_cast< QgsTemporalNavigationObject::NavigationMode>( QgsProject::instance()->readNumEntry( QStringLiteral( "TemporalControllerWidget" ),
314  QStringLiteral( "/NavigationMode" ), 0, &ok ) );
315  if ( ok )
316  {
317  mNavigationObject->setNavigationMode( mode );
318  setWidgetStateFromNavigationMode( mode );
319  }
320  else
321  {
323  setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::NavigationOff );
324  }
325 
326  const QString startString = QgsProject::instance()->readEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/StartDateTime" ) );
327  const QString endString = QgsProject::instance()->readEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/EndDateTime" ) );
328  if ( !startString.isEmpty() && !endString.isEmpty() )
329  {
330  whileBlocking( mStartDateTime )->setDateTime( QDateTime::fromString( startString, Qt::ISODate ) );
331  whileBlocking( mEndDateTime )->setDateTime( QDateTime::fromString( endString, Qt::ISODate ) );
332  whileBlocking( mFixedRangeStartDateTime )->setDateTime( QDateTime::fromString( startString, Qt::ISODate ) );
333  whileBlocking( mFixedRangeEndDateTime )->setDateTime( QDateTime::fromString( endString, Qt::ISODate ) );
334  }
335  else
336  {
337  setDatesToProjectTime();
338  }
339  updateTemporalExtent();
340  updateFrameDuration();
341 
342  mNavigationObject->setFramesPerSecond( QgsProject::instance()->timeSettings()->framesPerSecond() );
343  mNavigationObject->setTemporalRangeCumulative( QgsProject::instance()->timeSettings()->isTemporalRangeCumulative() );
344 }
345 
346 void QgsTemporalControllerWidget::mNavigationOff_clicked()
347 {
348  QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/NavigationMode" ),
349  static_cast<int>( QgsTemporalNavigationObject::NavigationOff ) );
350 
352  setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::NavigationOff );
353 }
354 
355 void QgsTemporalControllerWidget::mNavigationFixedRange_clicked()
356 {
357  QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/NavigationMode" ),
358  static_cast<int>( QgsTemporalNavigationObject::FixedRange ) );
359 
361  setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::FixedRange );
362 }
363 
364 void QgsTemporalControllerWidget::mNavigationAnimated_clicked()
365 {
366  QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ), QStringLiteral( "/NavigationMode" ),
367  static_cast<int>( QgsTemporalNavigationObject::Animated ) );
368 
370  setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::Animated );
371 }
372 
373 void QgsTemporalControllerWidget::setWidgetStateFromNavigationMode( const QgsTemporalNavigationObject::NavigationMode mode )
374 {
375  mNavigationOff->setChecked( mode == QgsTemporalNavigationObject::NavigationOff );
376  mNavigationFixedRange->setChecked( mode == QgsTemporalNavigationObject::FixedRange );
377  mNavigationAnimated->setChecked( mode == QgsTemporalNavigationObject::Animated );
378 
379  switch ( mode )
380  {
382  mNavigationModeStackedWidget->setCurrentIndex( 0 );
383  break;
385  mNavigationModeStackedWidget->setCurrentIndex( 1 );
386  break;
388  mNavigationModeStackedWidget->setCurrentIndex( 2 );
389  break;
390  }
391 }
392 
393 void QgsTemporalControllerWidget::onLayersAdded( const QList<QgsMapLayer *> &layers )
394 {
395  if ( !mHasTemporalLayersLoaded )
396  {
397  for ( QgsMapLayer *layer : layers )
398  {
399  if ( layer->temporalProperties() )
400  {
401  mHasTemporalLayersLoaded |= layer->temporalProperties()->isActive();
402 
403  if ( !mHasTemporalLayersLoaded )
404  {
405  connect( layer, &QgsMapLayer::dataSourceChanged, this, [ this, layer ]
406  {
407  if ( layer->isValid() && layer->temporalProperties()->isActive() && !mHasTemporalLayersLoaded )
408  {
409  mHasTemporalLayersLoaded = true;
410  firstTemporalLayerLoaded( layer );
411  }
412  } );
413  }
414 
415  firstTemporalLayerLoaded( layer );
416  }
417  }
418  }
419 }
420 
421 void QgsTemporalControllerWidget::firstTemporalLayerLoaded( QgsMapLayer *layer )
422 {
423  setDatesToProjectTime();
424 
425  QgsMeshLayer *meshLayer = qobject_cast<QgsMeshLayer *>( layer );
426  if ( meshLayer )
427  setTimeStep( meshLayer->firstValidTimeStep() );
428 }
429 
430 void QgsTemporalControllerWidget::onProjectCleared()
431 {
432  mHasTemporalLayersLoaded = false;
433 
435  setWidgetStateFromNavigationMode( QgsTemporalNavigationObject::NavigationOff );
436 
437  whileBlocking( mStartDateTime )->setDateTime( QDateTime( QDate::currentDate(), QTime( 0, 0, 0, Qt::UTC ) ) );
438  whileBlocking( mEndDateTime )->setDateTime( mStartDateTime->dateTime() );
439  whileBlocking( mFixedRangeStartDateTime )->setDateTime( QDateTime( QDate::currentDate(), QTime( 0, 0, 0, Qt::UTC ) ) );
440  whileBlocking( mFixedRangeEndDateTime )->setDateTime( mStartDateTime->dateTime() );
441  updateTemporalExtent();
442  mTimeStepsComboBox->setCurrentIndex( mTimeStepsComboBox->findData( QgsUnitTypes::TemporalHours ) );
443  mStepSpinBox->setValue( 1 );
444 }
445 
446 void QgsTemporalControllerWidget::updateSlider( const QgsDateTimeRange &range )
447 {
448  whileBlocking( mSlider )->setValue( mNavigationObject->currentFrameNumber() );
449  updateRangeLabel( range );
450 }
451 
452 void QgsTemporalControllerWidget::updateRangeLabel( const QgsDateTimeRange &range )
453 {
454  switch ( mNavigationObject->navigationMode() )
455  {
457  mCurrentRangeLabel->setText( tr( "Frame: %1 to %2" ).arg(
458  range.begin().toString( "yyyy-MM-dd HH:mm:ss" ),
459  range.end().toString( "yyyy-MM-dd HH:mm:ss" ) ) );
460  break;
462  mCurrentRangeLabel->setText( tr( "Range: %1 to %2" ).arg(
463  range.begin().toString( "yyyy-MM-dd HH:mm:ss" ),
464  range.end().toString( "yyyy-MM-dd HH:mm:ss" ) ) );
465  break;
467  mCurrentRangeLabel->setText( tr( "Temporal navigation disabled" ) );
468  break;
469  }
470 }
471 
473 {
474  return mNavigationObject;
475 }
476 
477 void QgsTemporalControllerWidget::settings_clicked()
478 {
479  QgsTemporalMapSettingsWidget *settingsWidget = new QgsTemporalMapSettingsWidget( this );
480  settingsWidget->setFrameRateValue( mNavigationObject->framesPerSecond() );
481  settingsWidget->setIsTemporalRangeCumulative( mNavigationObject->temporalRangeCumulative() );
482 
483  connect( settingsWidget, &QgsTemporalMapSettingsWidget::frameRateChanged, this, [ = ]( double rate )
484  {
485  // save new settings into project
487  mNavigationObject->setFramesPerSecond( rate );
488  } );
489 
490  connect( settingsWidget, &QgsTemporalMapSettingsWidget::temporalRangeCumulativeChanged, this, [ = ]( bool state )
491  {
492  // save new settings into project
494  mNavigationObject->setTemporalRangeCumulative( state );
495  } );
496  openPanel( settingsWidget );
497 }
498 
499 void QgsTemporalControllerWidget::timeSlider_valueChanged( int value )
500 {
501  mNavigationObject->setCurrentFrameNumber( value );
502 }
503 
504 void QgsTemporalControllerWidget::startEndDateTime_changed()
505 {
506  whileBlocking( mFixedRangeStartDateTime )->setDateTime( mStartDateTime->dateTime() );
507  whileBlocking( mFixedRangeEndDateTime )->setDateTime( mEndDateTime->dateTime() );
508 
509  updateTemporalExtent();
510  saveRangeToProject();
511 }
512 
513 void QgsTemporalControllerWidget::fixedRangeStartEndDateTime_changed()
514 {
515  whileBlocking( mStartDateTime )->setDateTime( mFixedRangeStartDateTime->dateTime() );
516  whileBlocking( mEndDateTime )->setDateTime( mFixedRangeEndDateTime->dateTime() );
517 
518  updateTemporalExtent();
519  saveRangeToProject();
520 }
521 
522 void QgsTemporalControllerWidget::mRangeSetToAllLayersAction_triggered()
523 {
524  setDatesToAllLayers();
525  saveRangeToProject();
526 }
527 
528 void QgsTemporalControllerWidget::setTimeStep( const QgsInterval &timeStep )
529 {
530  if ( ! timeStep.isValid() || timeStep.seconds() <= 0 )
531  return;
532 
533  // Search the time unit the most appropriate :
534  // the one that gives the smallest time step value for double spin box with round value (if possible) and/or the less signifiant digits
535 
536  int selectedUnit = -1;
537  int stringSize = std::numeric_limits<int>::max();
538  int precision = mStepSpinBox->decimals();
539  double selectedValue = std::numeric_limits<double>::max();
540  for ( int i = 0; i < mTimeStepsComboBox->count(); ++i )
541  {
542  QgsUnitTypes::TemporalUnit unit = static_cast<QgsUnitTypes::TemporalUnit>( mTimeStepsComboBox->itemData( i ).toInt() );
543  double value = timeStep.seconds() * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalSeconds, unit );
544  QString string = QString::number( value, 'f', precision );
545  string.remove( QRegExp( "0+$" ) ); //remove trailing zero
546  string.remove( QRegExp( "[.]+$" ) ); //remove last point if present
547 
548  if ( value >= 1
549  && string.size() <= stringSize // less significant digit than currently selected
550  && value < selectedValue ) // less than currently selected
551  {
552  selectedUnit = i;
553  selectedValue = value;
554  stringSize = string.size();
555  }
556  else if ( string != '0'
557  && string.size() < precision + 2 //round value (ex: 0.xx with precision=3)
558  && string.size() < stringSize ) //less significant digit than currently selected
559  {
560  selectedUnit = i ;
561  selectedValue = value ;
562  stringSize = string.size();
563  }
564  }
565 
566  if ( selectedUnit >= 0 )
567  {
568  mStepSpinBox->setValue( selectedValue );
569  mTimeStepsComboBox->setCurrentIndex( selectedUnit );
570  }
571 
572  updateFrameDuration();
573 }
574 
575 void QgsTemporalControllerWidget::mRangeSetToProjectAction_triggered()
576 {
577  setDatesToProjectTime();
578  saveRangeToProject();
579 }
580 
581 void QgsTemporalControllerWidget::setDates( const QgsDateTimeRange &range )
582 {
583  if ( range.begin().isValid() && range.end().isValid() )
584  {
585  whileBlocking( mStartDateTime )->setDateTime( range.begin() );
586  whileBlocking( mEndDateTime )->setDateTime( range.end() );
587  whileBlocking( mFixedRangeStartDateTime )->setDateTime( range.begin() );
588  whileBlocking( mFixedRangeEndDateTime )->setDateTime( range.end() );
589  updateTemporalExtent();
590  }
591 }
592 
593 void QgsTemporalControllerWidget::setDatesToAllLayers()
594 {
595  QgsDateTimeRange range;
597  setDates( range );
598 }
599 
600 void QgsTemporalControllerWidget::setDatesToProjectTime()
601 {
602  QgsDateTimeRange range;
603 
604  // by default try taking the project's fixed temporal extent
605  if ( QgsProject::instance()->timeSettings() )
607 
608  // if that's not set, calculate the extent from the project's layers
609  if ( !range.begin().isValid() || !range.end().isValid() )
610  {
612  }
613 
614  setDates( range );
615 }
616 
617 void QgsTemporalControllerWidget::saveRangeToProject()
618 {
619  QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ),
620  QStringLiteral( "/StartDateTime" ), mStartDateTime->dateTime().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) );
621  QgsProject::instance()->writeEntry( QStringLiteral( "TemporalControllerWidget" ),
622  QStringLiteral( "/EndDateTime" ), mEndDateTime->dateTime().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) );
623 }
QgsUnitTypes::TemporalDecades
@ TemporalDecades
Decades.
Definition: qgsunittypes.h:159
QgsTemporalNavigationObject::rewindToStart
void rewindToStart()
Rewinds the temporal navigation to start of the temporal extent.
Definition: qgstemporalnavigationobject.cpp:280
QgsProject::writeEntry
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean value to the project file.
Definition: qgsproject.cpp:2441
QgsTemporalControllerWidget::exportAnimation
void exportAnimation()
Triggered when an animation should be exported.
QgsTemporalNavigationObject::Animated
@ Animated
Temporal navigation relies on frames within a datetime range.
Definition: qgstemporalnavigationobject.h:54
QgsTemporalControllerWidget::temporalController
QgsTemporalController * temporalController()
Returns the temporal controller object used by this object in navigation.
Definition: qgstemporalcontrollerwidget.cpp:472
QgsProjectTimeSettings::setIsTemporalRangeCumulative
void setIsTemporalRangeCumulative(bool state)
Sets the project's temporal range as cumulative in animation settings.
Definition: qgsprojecttimesettings.cpp:131
QgsInterval::seconds
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:168
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
QgsProjectTimeSettings::setFramesPerSecond
void setFramesPerSecond(double rate)
Sets the project's default animation frame rate, in frames per second.
Definition: qgsprojecttimesettings.cpp:121
QgsProject::layersAdded
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.
QgsMeshLayer::firstValidTimeStep
QgsInterval firstValidTimeStep() const
Returns the first valid time step of the dataset groups, invalid QgInterval if no time step is presen...
Definition: qgsmeshlayer.cpp:817
qgstemporalmapsettingswidget.h
QgsTemporalNavigationObject::setTemporalRangeCumulative
void setTemporalRangeCumulative(bool state)
Sets the animation temporal range as cumulative.
Definition: qgstemporalnavigationobject.cpp:225
QgsTemporalNavigationObject::Reverse
@ Reverse
Animation is playing in reverse.
Definition: qgstemporalnavigationobject.h:62
qgsgui.h
QgsUnitTypes::TemporalYears
@ TemporalYears
Years.
Definition: qgsunittypes.h:158
QgsProject::cleared
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
QgsUnitTypes::TemporalDays
@ TemporalDays
Days.
Definition: qgsunittypes.h:155
QgsPanelWidget::openPanel
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
Definition: qgspanelwidget.cpp:79
QgsTemporalNavigationObject::navigationMode
NavigationMode navigationMode() const
Returns the currenttemporal navigation mode.
Definition: qgstemporalnavigationobject.h:92
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsProject::readProject
void readProject(const QDomDocument &)
Emitted when a project is being read.
QgsMapLayerTemporalProperties::calculateTemporalExtent
virtual QgsDateTimeRange calculateTemporalExtent(QgsMapLayer *layer) const
Attempts to calculate the overall temporal extent for the specified layer, using the settings defined...
Definition: qgsmaplayertemporalproperties.cpp:30
QgsMapLayerModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: qgsmaplayermodel.cpp:241
QgsProject::readEntry
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
Definition: qgsproject.cpp:2526
QgsTemporalNavigationObject::playForward
void playForward()
Starts the animation playing in a forward direction up till the end of all frames.
Definition: qgstemporalnavigationobject.cpp:246
QgsTemporalNavigationObject::navigationModeChanged
void navigationModeChanged(NavigationMode mode)
Emitted whenever the navigation mode changes.
QgsTemporalNavigationObject::setFrameDuration
void setFrameDuration(QgsInterval duration)
Sets the frame duration, which dictates the temporal length of each frame in the animation.
Definition: qgstemporalnavigationobject.cpp:185
QgsUnitTypes::TemporalUnit
TemporalUnit
Temporal units.
Definition: qgsunittypes.h:150
QgsTemporalNavigationObject::NavigationMode
NavigationMode
Represents the current temporal navigation mode.
Definition: qgstemporalnavigationobject.h:52
QgsTemporalNavigationObject::Forward
@ Forward
Animation is playing forward.
Definition: qgstemporalnavigationobject.h:61
QgsProjectTimeSettings::temporalRange
QgsDateTimeRange temporalRange() const
Returns the project's temporal range, which indicates the earliest and latest datetime ranges associa...
Definition: qgsprojecttimesettings.cpp:34
QgsUnitTypes::TemporalSeconds
@ TemporalSeconds
Seconds.
Definition: qgsunittypes.h:152
QgsTemporalNavigationObject::temporalExtentsChanged
void temporalExtentsChanged(const QgsDateTimeRange &extent)
Emitted whenever the temporalExtent extent changes.
qgstemporalcontrollerwidget.h
qgsapplication.h
QgsTemporalNavigationObject::setLooping
void setLooping(bool loop)
Sets whether the animation should loop after hitting the end or start frame.
Definition: qgstemporalnavigationobject.cpp:67
QgsUnitTypes::TemporalMonths
@ TemporalMonths
Months.
Definition: qgsunittypes.h:157
QgsProjectTimeSettings::timeStepUnit
QgsUnitTypes::TemporalUnit timeStepUnit() const
Returns the project's time step (length of one animation frame) unit, which is used as the default va...
Definition: qgsprojecttimesettings.cpp:101
QgsMapLayer::dataSourceChanged
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
QgsUnitTypes::fromUnitToUnitFactor
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
Definition: qgsunittypes.cpp:352
QgsTemporalControllerWidget::QgsTemporalControllerWidget
QgsTemporalControllerWidget(QWidget *parent=nullptr)
Constructor for QgsTemporalControllerWidget, with the specified parent widget.
Definition: qgstemporalcontrollerwidget.cpp:32
QgsMapLayerModel::rowCount
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Definition: qgsmaplayermodel.cpp:226
precision
int precision
Definition: qgswfsgetfeature.cpp:49
qgsmaplayertemporalproperties.h
QgsTemporalNavigationObject::playBackward
void playBackward()
Starts the animation playing in a reverse direction until the beginning of the time range.
Definition: qgstemporalnavigationobject.cpp:258
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:30
QgsTemporalProperty::isActive
bool isActive() const
Returns true if the temporal property is active.
Definition: qgstemporalproperty.cpp:36
QgsTemporalNavigationObject::skipToEnd
void skipToEnd()
Skips the temporal navigation to end of the temporal extent.
Definition: qgstemporalnavigationobject.cpp:285
QgsTemporalNavigationObject::stateChanged
void stateChanged(AnimationState state)
Emitted whenever the animation state changes.
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
QgsTemporalNavigationObject::setCurrentFrameNumber
void setCurrentFrameNumber(long long frame)
Sets the current animation frame number.
Definition: qgstemporalnavigationobject.cpp:168
QgsUnitTypes::toString
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
Definition: qgsunittypes.cpp:199
QgsMeshLayer
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
QgsTemporalNavigationObject::setTemporalExtents
void setTemporalExtents(const QgsDateTimeRange &extents)
Sets the navigation temporal extents, which dictate the earliest and latest date time possible in the...
Definition: qgstemporalnavigationobject.cpp:132
QgsTemporalNavigationObject::setFramesPerSecond
void setFramesPerSecond(double rate)
Sets the animation frame rate, in frames per second.
Definition: qgstemporalnavigationobject.cpp:211
QgsTemporalController
A controller base class for temporal objects, contains a signal for notifying updates of the objects ...
Definition: qgstemporalcontroller.h:42
QgsMapLayer::temporalProperties
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1203
QgsTemporalNavigationObject::Idle
@ Idle
Animation is paused.
Definition: qgstemporalnavigationobject.h:63
QgsTemporalNavigationObject::totalFrameCount
long long totalFrameCount()
Returns the total number of frames for the navigation.
Definition: qgstemporalnavigationobject.cpp:291
QgsTemporalNavigationObject
Implements a temporal controller based on a frame by frame navigation and animation.
Definition: qgstemporalnavigationobject.h:40
qgstemporalutils.h
QgsProject::timeSettings
const QgsProjectTimeSettings * timeSettings() const
Returns the project's time settings, which contains the project's temporal range and other time based...
Definition: qgsproject.cpp:3080
qgsmeshlayer.h
QgsUnitTypes::TemporalHours
@ TemporalHours
Hours.
Definition: qgsunittypes.h:154
qgsprojecttimesettings.h
QgsUnitTypes::TemporalMinutes
@ TemporalMinutes
Minutes.
Definition: qgsunittypes.h:153
QgsUnitTypes::TemporalMilliseconds
@ TemporalMilliseconds
Milliseconds.
Definition: qgsunittypes.h:151
QgsTemporalController::updateTemporalRange
void updateTemporalRange(const QgsDateTimeRange &range)
Signals that a temporal range has changed and needs to be updated in all connected objects.
QgsUnitTypes::TemporalCenturies
@ TemporalCenturies
Centuries.
Definition: qgsunittypes.h:160
QgsTemporalNavigationObject::pause
void pause()
Pauses the temporal navigation.
Definition: qgstemporalnavigationobject.cpp:240
qgsmaplayermodel.h
QgsTemporalNavigationObject::next
void next()
Advances to the next frame.
Definition: qgstemporalnavigationobject.cpp:270
QgsUnitTypes::TemporalWeeks
@ TemporalWeeks
Weeks.
Definition: qgsunittypes.h:156
QgsTemporalNavigationObject::temporalFrameDurationChanged
void temporalFrameDurationChanged(const QgsInterval &interval)
Emitted whenever the frameDuration interval of the controller changes.
QgsTemporalNavigationObject::temporalRangeCumulative
bool temporalRangeCumulative() const
Returns the animation temporal range cumulative settings.
Definition: qgstemporalnavigationobject.cpp:230
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsMapLayerModel::LayerRole
@ LayerRole
Stores pointer to the map layer itself.
Definition: qgsmaplayermodel.h:51
QgsInterval
A representation of the interval between two datetime values.
Definition: qgsinterval.h:41
QgsTemporalNavigationObject::previous
void previous()
Jumps back to the previous frame.
Definition: qgstemporalnavigationobject.cpp:275
QgsTemporalNavigationObject::NavigationOff
@ NavigationOff
Temporal navigation is disabled.
Definition: qgstemporalnavigationobject.h:53
QgsTemporalNavigationObject::AnimationState
AnimationState
Represents the current animation state.
Definition: qgstemporalnavigationobject.h:60
QgsTemporalControllerWidget::keyPressEvent
void keyPressEvent(QKeyEvent *e) override
Definition: qgstemporalcontrollerwidget.cpp:177
QgsMapLayerModel::index
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Definition: qgsmaplayermodel.cpp:200
QgsTemporalNavigationObject::currentFrameNumber
long long currentFrameNumber() const
Returns the current frame number.
Definition: qgstemporalnavigationobject.cpp:180
QgsInterval::isValid
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:181
QgsProjectTimeSettings::setTimeStep
void setTimeStep(double step)
Sets the project's time step (length of one animation frame), which is used as the default value when...
Definition: qgsprojecttimesettings.cpp:116
qgsproject.h
QgsProjectTimeSettings::setTimeStepUnit
void setTimeStepUnit(QgsUnitTypes::TemporalUnit unit)
Sets the project's time step (length of one animation frame) unit, which is used as the default value...
Definition: qgsprojecttimesettings.cpp:106
QgsTemporalNavigationObject::setNavigationMode
void setNavigationMode(const NavigationMode mode)
Sets the temporal navigation mode.
Definition: qgstemporalnavigationobject.cpp:107
QgsTemporalNavigationObject::FixedRange
@ FixedRange
Temporal navigation relies on a fixed datetime range.
Definition: qgstemporalnavigationobject.h:55
QgsProject::readNumEntry
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
Definition: qgsproject.cpp:2552
QgsMapLayerModel
The QgsMapLayerModel class is a model to display layers in widgets.
Definition: qgsmaplayermodel.h:37
QgsTemporalNavigationObject::framesPerSecond
double framesPerSecond() const
Returns the animation frame rate, in frames per second.
Definition: qgstemporalnavigationobject.cpp:220
QgsTemporalNavigationObject::animationState
AnimationState animationState() const
Returns the current animation state.
Definition: qgstemporalnavigationobject.cpp:306
QgsTemporalUtils::calculateTemporalRangeForProject
static QgsDateTimeRange calculateTemporalRangeForProject(QgsProject *project)
Calculates the temporal range for a project.
Definition: qgstemporalutils.cpp:31