1 /***************************************************************************
2       qgspostgresrastertemporalsettingswidget.cpp
3       ------------------
4     begin                : March 2021
5     copyright            : (C) 2021 by Nyall Dawson
6     email                : nyall dot dawson at gmail 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 "qgspostgresrastertemporalsettingswidget.h"
19 #include "qgsmaplayer.h"
20 #include "qgsproject.h"
21 #include "qgsrasterlayer.h"
22 #include "qgsprojecttimesettings.h"
23 #include "qgsrasterlayertemporalproperties.h"
24 #include "qgsproviderregistry.h"
25 #include "qgsprovidermetadata.h"
26 
QgsPostgresRasterTemporalSettingsWidget(QgsMapLayer * layer,QgsMapCanvas * canvas,QWidget * parent)27 QgsPostgresRasterTemporalSettingsWidget::QgsPostgresRasterTemporalSettingsWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
28   : QgsMapLayerConfigWidget( layer, canvas, parent )
29   , mRasterLayer( qobject_cast< QgsRasterLayer* >( layer ) )
30 {
31   Q_ASSERT( mRasterLayer );
32   Q_ASSERT( mRasterLayer->dataProvider() );
33   Q_ASSERT( mRasterLayer->providerType() == QLatin1String( "postgresraster" ) );
34 
35   setupUi( this );
36 
37   mPostgresRasterTemporalGroup->setEnabled( true );
38   mPostgresRasterTemporalGroup->setVisible( true );
39   mPostgresRasterTemporalGroup->setChecked( false );
40 
41   mPostgresRasterTemporalFieldComboBox->setFilters( QgsFieldProxyModel::Filter::Date |
42       QgsFieldProxyModel::Filter::DateTime |
43       QgsFieldProxyModel::Filter::String );
44   mPostgresRasterTemporalFieldComboBox->setAllowEmptyFieldName( true );
45   connect( mPostgresRasterTemporalFieldComboBox, &QgsFieldComboBox::fieldChanged, this, [ = ]( const QString & fieldName )
46   {
47     mPostgresRasterDefaultTime->setEnabled( ! fieldName.isEmpty() );
48   } );
49   mPostgresRasterDefaultTime->setAllowNull( true );
50   mPostgresRasterDefaultTime->setEmpty();
51   mDefaultTimeStackedWidget->setCurrentIndex( 0 );
52 
53   syncToLayer( mRasterLayer );
54 }
55 
syncToLayer(QgsMapLayer * layer)56 void QgsPostgresRasterTemporalSettingsWidget::syncToLayer( QgsMapLayer *layer )
57 {
58   mRasterLayer = qobject_cast< QgsRasterLayer * >( layer );
59   const QgsFields fields { mRasterLayer->dataProvider()->fields() };
60   mPostgresRasterTemporalFieldComboBox->setFields( fields );
61 
62   mDefaultTimeStackedWidget->setCurrentIndex( 0 );
63   mDefaultTimeComboBox->clear();
64 
65   if ( mRasterLayer->dataProvider()->uri().hasParam( QStringLiteral( "temporalFieldIndex" ) ) )
66   {
67     bool ok;
68     const int fieldIdx {  mRasterLayer->dataProvider()->uri().param( QStringLiteral( "temporalFieldIndex" ) ).toInt( &ok ) };
69     if ( ok && fields.exists( fieldIdx ) )
70     {
71       mPostgresRasterTemporalGroup->setChecked( true );
72       mPostgresRasterTemporalFieldComboBox->setField( fields.field( fieldIdx ).name() );
73 
74       const QList< QgsDateTimeRange > allRanges = mRasterLayer->dataProvider()->temporalCapabilities()->allAvailableTemporalRanges();
75       if ( !allRanges.empty() && allRanges.size() < 50 )
76       {
77         // if an appropriate number of unique ranges is known, show a combo box with these options instead of the free-form
78         // date picker widget
79         mDefaultTimeStackedWidget->setCurrentIndex( 1 );
80         for ( const QgsDateTimeRange &range : allRanges )
81         {
82           mDefaultTimeComboBox->addItem( range.begin().toString( Qt::ISODate ), QVariant::fromValue( range.begin() ) );
83         }
84       }
85 
86       if ( mRasterLayer->dataProvider()->uri().hasParam( QStringLiteral( "temporalDefaultTime" ) ) )
87       {
88         const QDateTime defaultDateTime { QDateTime::fromString( mRasterLayer->dataProvider()->uri().param( QStringLiteral( "temporalDefaultTime" ) ), Qt::DateFormat::ISODate ) };
89         if ( defaultDateTime.isValid() )
90         {
91           mPostgresRasterDefaultTime->setDateTime( defaultDateTime );
92 
93           if ( const int index = mDefaultTimeComboBox->findData( QVariant::fromValue( defaultDateTime ) ); index >= 0 )
94             mDefaultTimeComboBox->setCurrentIndex( index );
95           else if ( mDefaultTimeComboBox->count() > 0 )
96             mDefaultTimeComboBox->setCurrentIndex( 0 );
97         }
98       }
99     }
100   }
101 }
102 
apply()103 void QgsPostgresRasterTemporalSettingsWidget::apply()
104 {
105   QgsDataSourceUri uri { mRasterLayer->dataProvider()->uri() };
106   if ( mPostgresRasterTemporalGroup->isEnabled() &&
107        mPostgresRasterTemporalGroup->isChecked() &&
108        ! mPostgresRasterTemporalFieldComboBox->currentField().isEmpty() )
109   {
110     const QString originaUri { uri.uri() };
111     const int fieldIdx { mRasterLayer->dataProvider()->fields().lookupField( mPostgresRasterTemporalFieldComboBox->currentField() ) };
112     uri.removeParam( QStringLiteral( "temporalFieldIndex" ) );
113     uri.removeParam( QStringLiteral( "temporalDefaultTime" ) );
114     if ( fieldIdx >= 0 )
115     {
116       uri.setParam( QStringLiteral( "temporalFieldIndex" ), QString::number( fieldIdx ) );
117 
118       QDateTime defaultDateTime;
119       if ( mDefaultTimeStackedWidget->currentIndex() == 0 )
120       {
121         if ( mPostgresRasterDefaultTime->dateTime().isValid() )
122         {
123           defaultDateTime = mPostgresRasterDefaultTime->dateTime();
124         }
125       }
126       else
127       {
128         if ( mDefaultTimeComboBox->currentData().isValid() )
129         {
130           defaultDateTime = mDefaultTimeComboBox->currentData().value< QDateTime >();
131         }
132       }
133 
134       if ( defaultDateTime.isValid() )
135       {
136         const QTime defaultTime { defaultDateTime.time() };
137         // Set secs to 0
138         defaultDateTime.setTime( { defaultTime.hour(), defaultTime.minute(), 0 } );
139         uri.setParam( QStringLiteral( "temporalDefaultTime" ), defaultDateTime.toString( Qt::DateFormat::ISODate ) );
140       }
141 
142       if ( uri.uri( ) != originaUri )
143         mRasterLayer->setDataSource( uri.uri(), mRasterLayer->name(), mRasterLayer->providerType(), QgsDataProvider::ProviderOptions() );
144     }
145   }
146   else if ( uri.hasParam( QStringLiteral( "temporalFieldIndex" ) ) )
147   {
148     uri.removeParam( QStringLiteral( "temporalFieldIndex" ) );
149     uri.removeParam( QStringLiteral( "temporalDefaultTime" ) );
150     mRasterLayer->setDataSource( uri.uri(), mRasterLayer->name(), mRasterLayer->providerType(), QgsDataProvider::ProviderOptions() );
151   }
152 }
153 
154 
155 //
156 // QgsPostgresRasterTemporalSettingsConfigWidgetFactory
157 //
158 
supportLayerPropertiesDialog() const159 bool QgsPostgresRasterTemporalSettingsConfigWidgetFactory::supportLayerPropertiesDialog() const
160 {
161   return true;
162 }
163 
supportsLayer(QgsMapLayer * layer) const164 bool QgsPostgresRasterTemporalSettingsConfigWidgetFactory::supportsLayer( QgsMapLayer *layer ) const
165 {
166   return layer && layer->isValid() && layer->providerType() == QLatin1String( "postgresraster" );
167 }
168 
parentPage() const169 QgsMapLayerConfigWidgetFactory::ParentPage QgsPostgresRasterTemporalSettingsConfigWidgetFactory::parentPage() const
170 {
171   return ParentPage::Temporal;
172 }
173 
createWidget(QgsMapLayer * layer,QgsMapCanvas * canvas,bool,QWidget * parent) const174 QgsMapLayerConfigWidget *QgsPostgresRasterTemporalSettingsConfigWidgetFactory::createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool, QWidget *parent ) const
175 {
176   return new QgsPostgresRasterTemporalSettingsWidget( layer, canvas, parent );
177 }
178