[mesh] Fixes time reference for mesh layer #32186 #33399 #31933 (#33410)

[FEATURE] [mesh] Load reference time from MDAL when available

Also adds combo box to let the user set the provider time unit. This is particularly useful when MDAL incorrectly recognised the time units in the raw dat format (for example format does nof follow spec and it is not possible to determine if time interval is in hours or days)

fix #32186 
fix #33399
fix #31933
This commit is contained in:
Vincent Cloarec 2019-12-17 09:56:49 -04:00 committed by Peter Petrik
parent ead3b62c1e
commit c252a3ad4c
16 changed files with 296 additions and 25 deletions

View File

@ -417,6 +417,7 @@ Constructs an empty metadata object
double minimum,
double maximum,
int maximumVerticalLevels,
const QDateTime &referenceTime,
const QMap<QString, QString> &extraOptions );
%Docstring
Constructs a valid metadata object
@ -427,6 +428,7 @@ Constructs a valid metadata object
:param minimum: minimum value (magnitude for vectors) present among all group's dataset values
:param maximum: maximum value (magnitude for vectors) present among all group's dataset values
:param maximumVerticalLevels: maximum number of vertical levels for 3d stacked meshes, 0 for 2d meshes
:param referenceTime: reference time of the dataset group
:param extraOptions: dataset's extra options stored by the provider. Usually contains the name, time value, time units, data file vendor, ...
%End
@ -471,6 +473,13 @@ Returns maximum scalar value/vector magnitude present for whole dataset group
%Docstring
Returns maximum number of vertical levels for 3d stacked meshes
.. versionadded:: 3.12
%End
QDateTime referenceTime() const;
%Docstring
Returns the reference time
.. versionadded:: 3.12
%End

View File

@ -23,10 +23,20 @@ Represents a mesh time settings for mesh datasets
#include "qgsmeshtimesettings.h"
%End
public:
enum TimeUnit
{
//! second unit
seconds,
//! minute unit
minutes,
//! hour unit
hours,
//! day unit
days
};
QgsMeshTimeSettings();
%Docstring
Default constructor for relative time format and 0 offset
%End
QgsMeshTimeSettings( double relativeTimeOffsetHours, const QString &relativeTimeFormat );
%Docstring
Constructs relative time format settings with defined offset in hours
@ -88,6 +98,20 @@ Returns format used for absolute time
void setAbsoluteTimeFormat( const QString &absoluteTimeFormat );
%Docstring
Sets format used for absolute time
%End
TimeUnit providerTimeUnit() const;
%Docstring
Returns the provider time unit
.. versionadded:: 3.12
%End
void setProviderTimeUnit( const TimeUnit &providerTimeUnit );
%Docstring
Sets the provider time unit
.. versionadded:: 3.12
%End
};

View File

@ -120,6 +120,7 @@ void QgsMeshRendererActiveDatasetWidget::setTimeRange()
// update combobox
mTimeComboBox->blockSignals( true );
int currentIndex = mTimeComboBox->currentIndex();
mTimeComboBox->clear();
if ( groupWithMaximumDatasets > -1 )
{
@ -131,8 +132,9 @@ void QgsMeshRendererActiveDatasetWidget::setTimeRange()
mTimeComboBox->addItem( mMeshLayer->formatTime( time ), time );
}
}
mTimeComboBox->setCurrentIndex( currentIndex );
mTimeComboBox->blockSignals( false );
updateMetadata();
// enable/disable time controls depending on whether the data set is time varying
enableTimeControls();
}

View File

@ -17,6 +17,7 @@
#include "qgsmeshtimeformatdialog.h"
#include "qgsgui.h"
#include "qgsmeshtimesettings.h"
#include "qgsmeshlayerutils.h"
QgsMeshTimeFormatDialog::QgsMeshTimeFormatDialog( QgsMeshLayer *meshLayer, QWidget *parent, Qt::WindowFlags f )
: QDialog( parent, f ),
@ -30,14 +31,24 @@ QgsMeshTimeFormatDialog::QgsMeshTimeFormatDialog( QgsMeshLayer *meshLayer, QWidg
loadSettings();
mReloadReferenceTimeButton->setEnabled( layerHasReferenceTime() );
connect( mUseTimeComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mReferenceDateTimeEdit, &QDateTimeEdit::timeChanged, this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mReferenceDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mAbsoluteTimeFormatComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mUseTimeComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mRelativeTimeFormatComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mOffsetHoursSpinBox, qgis::overload<double>::of( &QDoubleSpinBox::valueChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mPlaybackIntervalSpinBox, qgis::overload<double>::of( &QDoubleSpinBox::valueChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mProviderTimeUnitComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsMeshTimeFormatDialog::saveSettings );
connect( mReloadReferenceTimeButton, &QPushButton::clicked, this, &QgsMeshTimeFormatDialog::loadProviderReferenceTime );
}
void QgsMeshTimeFormatDialog::loadProviderReferenceTime()
{
mReferenceDateTimeEdit->setDateTime( QgsMeshLayerUtils::firstReferenceTime( mLayer ) );
}
void QgsMeshTimeFormatDialog::loadSettings()
@ -54,7 +65,12 @@ void QgsMeshTimeFormatDialog::loadSettings()
mUseTimeComboBox->setCurrentIndex( 1 );
}
mReferenceDateTimeEdit->setDateTime( settings.absoluteTimeReferenceTime() );
// Sets the reference time, if not valid, sets the current date + time 00:00:00
if ( settings.absoluteTimeReferenceTime().isValid() )
mReferenceDateTimeEdit->setDateTime( settings.absoluteTimeReferenceTime() );
else
mReferenceDateTimeEdit->setDateTime( QDateTime( QDate::currentDate(), QTime( 00, 00, 00 ) ) );
mReferenceDateTimeEdit->setDisplayFormat( settings.absoluteTimeFormat() );
int index = mAbsoluteTimeFormatComboBox->findText( settings.absoluteTimeFormat() );
@ -75,6 +91,8 @@ void QgsMeshTimeFormatDialog::loadSettings()
mOffsetHoursSpinBox->setValue( settings.relativeTimeOffsetHours() );
mPlaybackIntervalSpinBox->setValue( settings.datasetPlaybackInterval() );
mProviderTimeUnitComboBox->setCurrentIndex( settings.providerTimeUnit() );
}
void QgsMeshTimeFormatDialog::saveSettings()
@ -86,6 +104,7 @@ void QgsMeshTimeFormatDialog::saveSettings()
settings.setRelativeTimeOffsetHours( mOffsetHoursSpinBox->value() );
settings.setRelativeTimeFormat( mRelativeTimeFormatComboBox->currentText() );
settings.setDatasetPlaybackInterval( mPlaybackIntervalSpinBox->value() );
settings.setProviderTimeUnit( static_cast<QgsMeshTimeSettings::TimeUnit>( mProviderTimeUnitComboBox->currentIndex() ) );
enableGroups( settings.useAbsoluteTime() ) ;
mLayer->setTimeSettings( settings );
}
@ -96,4 +115,9 @@ void QgsMeshTimeFormatDialog::enableGroups( bool useAbsoluteTime )
mRelativeTimeGroupBox->setEnabled( ! useAbsoluteTime );
}
bool QgsMeshTimeFormatDialog::layerHasReferenceTime() const
{
return QgsMeshLayerUtils::firstReferenceTime( mLayer ).isValid();
}
QgsMeshTimeFormatDialog::~QgsMeshTimeFormatDialog() = default;

View File

@ -39,12 +39,14 @@ class APP_EXPORT QgsMeshTimeFormatDialog: public QDialog, private Ui::QgsMeshTim
~QgsMeshTimeFormatDialog();
private slots:
void loadProviderReferenceTime();
private:
void loadSettings();
void saveSettings();
void enableGroups( bool useAbsoluteTime );
bool layerHasReferenceTime() const;
QgsMeshLayer *mLayer;
};

View File

@ -133,6 +133,7 @@ QgsMeshDatasetGroupMetadata::QgsMeshDatasetGroupMetadata( const QString &name,
double minimum,
double maximum,
int maximumVerticalLevels,
const QDateTime &referenceTime,
const QMap<QString, QString> &extraOptions )
: mName( name )
, mIsScalar( isScalar )
@ -141,6 +142,7 @@ QgsMeshDatasetGroupMetadata::QgsMeshDatasetGroupMetadata( const QString &name,
, mMaximumValue( maximum )
, mExtraOptions( extraOptions )
, mMaximumVerticalLevelsCount( maximumVerticalLevels )
, mReferenceTime( referenceTime )
{
}
@ -184,6 +186,11 @@ int QgsMeshDatasetGroupMetadata::maximumVerticalLevelsCount() const
return mMaximumVerticalLevelsCount;
}
QDateTime QgsMeshDatasetGroupMetadata::referenceTime() const
{
return mReferenceTime;
}
int QgsMeshDatasetSourceInterface::datasetCount( QgsMeshDatasetIndex index ) const
{
return datasetCount( index.group() );

View File

@ -388,6 +388,7 @@ class CORE_EXPORT QgsMeshDatasetGroupMetadata
* \param minimum minimum value (magnitude for vectors) present among all group's dataset values
* \param maximum maximum value (magnitude for vectors) present among all group's dataset values
* \param maximumVerticalLevels maximum number of vertical levels for 3d stacked meshes, 0 for 2d meshes
* \param referenceTime reference time of the dataset group
* \param extraOptions dataset's extra options stored by the provider. Usually contains the name, time value, time units, data file vendor, ...
*/
QgsMeshDatasetGroupMetadata( const QString &name,
@ -396,6 +397,7 @@ class CORE_EXPORT QgsMeshDatasetGroupMetadata
double minimum,
double maximum,
int maximumVerticalLevels,
const QDateTime &referenceTime,
const QMap<QString, QString> &extraOptions );
/**
@ -442,6 +444,13 @@ class CORE_EXPORT QgsMeshDatasetGroupMetadata
*/
int maximumVerticalLevelsCount() const;
/**
* Returns the reference time
*
* \since QGIS 3.12
*/
QDateTime referenceTime() const;
private:
QString mName;
bool mIsScalar = false;
@ -450,6 +459,7 @@ class CORE_EXPORT QgsMeshDatasetGroupMetadata
double mMaximumValue = std::numeric_limits<double>::quiet_NaN();
QMap<QString, QString> mExtraOptions;
int mMaximumVerticalLevelsCount = 0; // for 3d stacked meshes
QDateTime mReferenceTime;
};
/**

View File

@ -531,6 +531,13 @@ bool QgsMeshLayer::setDataProvider( QString const &provider, const QgsDataProvid
mDataSource = mDataSource + QStringLiteral( "&uid=%1" ).arg( QUuid::createUuid().toString() );
}
QDateTime referenceTime = QgsMeshLayerUtils::firstReferenceTime( this );
if ( referenceTime.isValid() )
{
mTimeSettings.setAbsoluteTimeReferenceTime( referenceTime );
mTimeSettings.setUseAbsoluteTime( true );
}
for ( int i = 0; i < mDataProvider->datasetGroupCount(); ++i )
assignDefaultStyleToDatasetGroup( i );

View File

@ -260,6 +260,22 @@ QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const
QString QgsMeshLayerUtils::formatTime( double hours, const QgsMeshTimeSettings &settings )
{
QString ret;
switch ( settings.providerTimeUnit() )
{
case QgsMeshTimeSettings::seconds:
hours = hours / 3600.0;
break;
case QgsMeshTimeSettings::minutes:
hours = hours / 60.0;
break;
case QgsMeshTimeSettings::hours:
break;
case QgsMeshTimeSettings::days:
hours = hours * 24.0;
break;
}
if ( settings.useAbsoluteTime() )
{
QString format( settings.absoluteTimeFormat() );
@ -346,4 +362,25 @@ QString QgsMeshLayerUtils::formatTime( double hours, const QgsMeshTimeSettings &
return ret;
}
QDateTime QgsMeshLayerUtils::firstReferenceTime( QgsMeshLayer *meshLayer )
{
if ( !meshLayer )
return QDateTime();
QgsMeshDataProvider *provider = meshLayer->dataProvider();
if ( !provider )
return QDateTime();
// Searches for the first valid reference time in the dataset groups
for ( int i = 0; i < provider->datasetGroupCount(); ++i )
{
QgsMeshDatasetGroupMetadata meta = provider->datasetGroupMetadata( i );
if ( meta.referenceTime().isValid() )
return meta.referenceTime();
}
return QDateTime();
}
///@endcond

View File

@ -178,6 +178,15 @@ class CORE_EXPORT QgsMeshLayerUtils
* Formats hours in human readable string based on settings
*/
static QString formatTime( double hours, const QgsMeshTimeSettings &settings );
/**
* Searches and returns the first valid reference time in layer's dataset group
* \param meshLayer mesh layer to parse
*
* \since QGIS 3.12
*/
static QDateTime firstReferenceTime( QgsMeshLayer *meshLayer );
};
///@endcond

View File

@ -40,6 +40,7 @@ QDomElement QgsMeshTimeSettings::writeXml( QDomDocument &doc, const QgsReadWrite
elem.setAttribute( QStringLiteral( "relative-time-format" ), mRelativeTimeFormat );
elem.setAttribute( QStringLiteral( "absolute-time-reference-time" ), mAbsoluteTimeReferenceTime.toString() );
elem.setAttribute( QStringLiteral( "absolute-time-format" ), mAbsoluteTimeFormat );
elem.setAttribute( QStringLiteral( "provider-time-unit" ), mProviderTimeUnit );
return elem;
}
@ -51,6 +52,7 @@ void QgsMeshTimeSettings::readXml( const QDomElement &elem, const QgsReadWriteCo
mRelativeTimeFormat = elem.attribute( QStringLiteral( "relative-time-format" ) );
mAbsoluteTimeReferenceTime = QDateTime::fromString( elem.attribute( QStringLiteral( "absolute-time-reference-time" ) ) );
mAbsoluteTimeFormat = elem.attribute( QStringLiteral( "absolute-time-format" ) );
mProviderTimeUnit = static_cast<QgsMeshTimeSettings::TimeUnit>( elem.attribute( QStringLiteral( "provider-time-unit" ) ).toInt() );
}
bool QgsMeshTimeSettings::useAbsoluteTime() const
@ -113,3 +115,13 @@ void QgsMeshTimeSettings::setAbsoluteTimeFormat( const QString &absoluteTimeForm
{
mAbsoluteTimeFormat = absoluteTimeFormat;
}
QgsMeshTimeSettings::TimeUnit QgsMeshTimeSettings::providerTimeUnit() const
{
return mProviderTimeUnit;
}
void QgsMeshTimeSettings::setProviderTimeUnit( const QgsMeshTimeSettings::TimeUnit &providerTimeUnit )
{
mProviderTimeUnit = providerTimeUnit;
}

View File

@ -35,7 +35,23 @@
class CORE_EXPORT QgsMeshTimeSettings
{
public:
//! Default constructor for relative time format and 0 offset
/**
* Time units used to display time
* \since QGIS 3.12
*/
enum TimeUnit
{
//! second unit
seconds = 0,
//! minute unit
minutes,
//! hour unit
hours,
//! day unit
days
};
QgsMeshTimeSettings();
//! Constructs relative time format settings with defined offset in hours
QgsMeshTimeSettings( double relativeTimeOffsetHours, const QString &relativeTimeFormat );
@ -84,6 +100,18 @@ class CORE_EXPORT QgsMeshTimeSettings
//! Sets format used for absolute time
void setAbsoluteTimeFormat( const QString &absoluteTimeFormat );
/**
* Returns the provider time unit
* \since QGIS 3.12
*/
TimeUnit providerTimeUnit() const;
/**
* Sets the provider time unit
* \since QGIS 3.12
*/
void setProviderTimeUnit( const TimeUnit &providerTimeUnit );
private:
bool mUseAbsoluteTime = false;
@ -93,6 +121,8 @@ class CORE_EXPORT QgsMeshTimeSettings
QDateTime mAbsoluteTimeReferenceTime;
QString mAbsoluteTimeFormat = QStringLiteral( "dd.MM.yyyy hh:mm:ss" );
TimeUnit mProviderTimeUnit = TimeUnit::hours;
};
Q_DECLARE_METATYPE( QgsMeshTimeSettings );

View File

@ -387,6 +387,7 @@ QgsMeshDatasetGroupMetadata QgsMeshMemoryDatasetGroup::groupMetadata() const
minimum,
maximum,
0,
QDateTime(),
metadata
);
}

View File

@ -493,6 +493,9 @@ QgsMeshDatasetGroupMetadata QgsMdalProvider::datasetGroupMetadata( int groupInde
metadata[key] = value;
}
QString referenceTimeString( MDAL_G_referenceTime( group ) );
QDateTime referenceTime = QDateTime::fromString( referenceTimeString, Qt::ISODate );
QgsMeshDatasetGroupMetadata meta(
name,
isScalar,
@ -500,6 +503,7 @@ QgsMeshDatasetGroupMetadata QgsMdalProvider::datasetGroupMetadata( int groupInde
min,
max,
maximumVerticalLevels,
referenceTime,
metadata
);

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>365</width>
<height>749</height>
<width>340</width>
<height>773</height>
</rect>
</property>
<property name="windowTitle">
@ -409,6 +409,9 @@
<string>Traces</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="topMargin">
@ -450,6 +453,24 @@
</item>
<item>
<widget class="QDoubleSpinBox" name="mTracesMaxLengthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>

View File

@ -13,6 +13,9 @@
<property name="windowTitle">
<string>Time Display Options</string>
</property>
<property name="toolTip">
<string>Reload from layer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QComboBox" name="mUseTimeComboBox">
@ -57,21 +60,48 @@
</widget>
</item>
<item>
<widget class="QDateTimeEdit" name="mReferenceDateTimeEdit">
<property name="dateTime">
<datetime>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
<year>2019</year>
<month>1</month>
<day>1</day>
</datetime>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
<item>
<widget class="QDateTimeEdit" name="mReferenceDateTimeEdit">
<property name="dateTime">
<datetime>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
<year>2019</year>
<month>1</month>
<day>1</day>
</datetime>
</property>
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mReloadReferenceTimeButton">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionRefresh.svg</normaloff>:/images/themes/default/mActionRefresh.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
@ -284,9 +314,51 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Provider time settings</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="1">
<widget class="QComboBox" name="mProviderTimeUnitComboBox">
<item>
<property name="text">
<string>seconds</string>
</property>
</item>
<item>
<property name="text">
<string>minutes</string>
</property>
</item>
<item>
<property name="text">
<string>hours</string>
</property>
</item>
<item>
<property name="text">
<string>days</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Time unit</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<resources>
<include location="../../../images/images.qrc"/>
</resources>
<connections/>
<slots>
<slot>shaftLengthMethodChanged()</slot>