1 /***************************************************************************
2   qgsnewmeshlayerdialog.cpp - QgsNewMeshLayerDialog
3 
4  ---------------------
5  begin                : 22.6.2021
6  copyright            : (C) 2021 by Vincent Cloarec
7  email                : vcloarec at gmail dot com
8  ***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 #include "qgsnewmeshlayerdialog.h"
17 
18 #include <QPushButton>
19 #include <QMessageBox>
20 
21 #include "qgsprovidermetadata.h"
22 #include "qgsproviderregistry.h"
23 #include "qgsmeshdataprovider.h"
24 #include "qgsproject.h"
25 #include "qgsmeshlayer.h"
26 #include "qgsapplication.h"
27 #include "qgshelp.h"
28 #include "qgsgui.h"
29 
30 
QgsNewMeshLayerDialog(QWidget * parent,Qt::WindowFlags fl)31 QgsNewMeshLayerDialog::QgsNewMeshLayerDialog( QWidget *parent, Qt::WindowFlags fl ) : QDialog( parent, fl )
32 {
33   QgsProviderMetadata *meta = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "mdal" ) );
34 
35   if ( !meta )
36   {
37     setLayout( new QVBoxLayout );
38     layout()->addWidget( new QLabel( tr( "MDAL not available, unable to create a new mesh layer" ) ) );
39     return;
40   }
41 
42   setupUi( this );
43   QgsGui::enableAutoGeometryRestore( this );
44   const QList<QgsMeshDriverMetadata> driverList = meta->meshDriversMetadata();
45 
46   for ( const QgsMeshDriverMetadata &driverMeta : driverList )
47     if ( driverMeta.capabilities() & QgsMeshDriverMetadata::CanWriteMeshData )
48     {
49       const QString description = driverMeta.description();
50       const QString driverName = driverMeta.name();
51       const QString suffix = driverMeta.writeMeshFrameOnFileSuffix();
52       mFormatComboBox->addItem( description, driverName );
53       mDriverSuffixes.insert( driverMeta.name(), suffix );
54       mDriverFileFilters.insert( driverMeta.name(), tr( "%1" ).arg( description ) + QStringLiteral( " (*." ) + suffix + ')' );
55     }
56 
57   const QStringList filters = mDriverFileFilters.values();
58   mFormatComboBox->setCurrentIndex( -1 );
59   mFileWidget->setStorageMode( QgsFileWidget::SaveFile );
60   mFileWidget->setFilter( filters.join( QLatin1String( ";;" ) ) );
61   mMeshProjectComboBox->setFilters( QgsMapLayerProxyModel::MeshLayer );
62 
63   connect( mFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ),
64            this, &QgsNewMeshLayerDialog::onFormatChanged );
65   connect( mFileWidget, &QgsFileWidget::fileChanged, this, &QgsNewMeshLayerDialog::onFilePathChanged );
66   connect( mInitializeMeshGroupBox, &QGroupBox::toggled, this, &QgsNewMeshLayerDialog::updateDialog );
67   connect( mMeshFileRadioButton, &QRadioButton::toggled, this, &QgsNewMeshLayerDialog::updateDialog );
68   connect( mMeshFromFileWidget, &QgsFileWidget::fileChanged, this, &QgsNewMeshLayerDialog::updateDialog );
69   connect( mMeshProjectComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsNewMeshLayerDialog::updateDialog );
70 
71   connect( buttonBox, &QDialogButtonBox::helpRequested, this, [ = ]
72   {
73     QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-mesh-layer" ) );
74   } );
75 
76   updateDialog();
77 }
78 
setCrs(const QgsCoordinateReferenceSystem & crs)79 void QgsNewMeshLayerDialog::setCrs( const QgsCoordinateReferenceSystem &crs )
80 {
81   mProjectionSelectionWidget->setCrs( crs );
82 }
83 
setSourceMeshLayer(QgsMeshLayer * meshLayer,bool fromExistingAsDefault)84 void QgsNewMeshLayerDialog::setSourceMeshLayer( QgsMeshLayer *meshLayer, bool fromExistingAsDefault )
85 {
86   mMeshProjectComboBox->setLayer( meshLayer );
87   mMeshProjectRadioButton->setChecked( true );
88   mInitializeMeshGroupBox->setChecked( fromExistingAsDefault );
89 }
90 
accept()91 void QgsNewMeshLayerDialog::accept()
92 {
93   if ( apply() )
94     QDialog::accept();
95 }
96 
updateDialog()97 void QgsNewMeshLayerDialog::updateDialog()
98 {
99   updateSourceMeshframe();
100 
101   buttonBox->button( QDialogButtonBox::Ok )->setEnabled(
102     ! mFileWidget->filePath().isEmpty() &&
103     mFormatComboBox->currentIndex() != -1 &&
104     mSourceMeshFrameReady );
105 }
106 
updateSourceMeshframe()107 void QgsNewMeshLayerDialog::updateSourceMeshframe()
108 {
109   mMeshProjectComboBox->setEnabled( false );
110   mMeshFromFileWidget->setEnabled( false );
111   if ( !mInitializeMeshGroupBox->isChecked() )
112   {
113     mSourceMeshFromFile.reset();
114     mSourceMeshFrameReady = true;
115     mProjectionSelectionWidget->setEnabled( true );
116   }
117   else if ( mMeshProjectRadioButton->isChecked() )
118   {
119     mMeshProjectComboBox->setEnabled( true );
120     mSourceMeshFromFile.reset();
121     mSourceMeshFrameReady = mMeshProjectComboBox->currentLayer() != nullptr;
122     mProjectionSelectionWidget->setEnabled( false );
123   }
124   else if ( mMeshFileRadioButton->isChecked() )
125   {
126     mMeshFromFileWidget->setEnabled( true );
127     if ( !mSourceMeshFromFile || mSourceMeshFromFile->source() != mMeshFromFileWidget->filePath() )
128     {
129       QgsApplication::setOverrideCursor( Qt::WaitCursor );
130       if ( !mMeshFromFileWidget->filePath().isEmpty() )
131         mSourceMeshFromFile.reset( new QgsMeshLayer( mMeshFromFileWidget->filePath(), QString(), QStringLiteral( "mdal" ) ) );
132 
133       if ( mSourceMeshFromFile && !mSourceMeshFromFile->isValid() )
134         mSourceMeshFromFile.reset();
135 
136       mProjectionSelectionWidget->setEnabled( false );
137 
138       mSourceMeshFrameReady = mSourceMeshFromFile != nullptr;
139 
140       QgsApplication::restoreOverrideCursor();
141     }
142   }
143   updateSourceMeshInformation();
144 }
145 
onFormatChanged()146 void QgsNewMeshLayerDialog::onFormatChanged()
147 {
148   const QString currentDriverName = mFormatComboBox->currentData().toString();
149   if ( currentDriverName.isEmpty() )
150     return;
151 
152   const QString currentFilter = mDriverFileFilters.value( currentDriverName );
153   mFileWidget->setSelectedFilter( currentFilter );
154 
155   const QString newSuffix = mDriverSuffixes.value( currentDriverName );
156 
157   QString currentFilePath = mFileWidget->filePath();
158   if ( currentFilePath.isEmpty() )
159     return;
160   const QFileInfo fileInfo( currentFilePath );
161   const QString currentSuffix = fileInfo.suffix();
162 
163   if ( !currentSuffix.isEmpty() )
164     currentFilePath =  currentFilePath.mid( 0, currentFilePath.lastIndexOf( '.' ) );
165 
166   if ( currentFilePath.right( 1 ) == QString( '.' ) )
167     currentFilePath.remove( currentFilePath.count() - 1, 1 );
168 
169   currentFilePath.append( '.' + newSuffix );
170 
171   mFileWidget->setFilePath( currentFilePath );
172 
173   updateDialog();
174 }
175 
onFilePathChanged()176 void QgsNewMeshLayerDialog::onFilePathChanged()
177 {
178   const QFileInfo fileInfo( mFileWidget->filePath() );
179   const QString &currentSuffix = fileInfo.suffix();
180 
181   const QStringList drivers = mDriverSuffixes.keys();
182   for ( const QString &driverName : drivers )
183   {
184     if ( mDriverSuffixes.value( driverName ) == currentSuffix )
185     {
186       whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( driverName ) );
187     }
188   }
189 
190   updateDialog();
191 }
192 
updateSourceMeshInformation()193 void QgsNewMeshLayerDialog::updateSourceMeshInformation()
194 {
195   QString myStyle = QgsApplication::reportStyleSheet();
196   myStyle.append( QStringLiteral( "body { margin: 10px; }\n " ) );
197 
198   mInformationTextBrowser->clear();
199   mInformationTextBrowser->document()->setDefaultStyleSheet( myStyle );
200   if ( mInitializeMeshGroupBox->isChecked() )
201   {
202     if ( mMeshProjectRadioButton->isChecked() )
203     {
204       if ( mMeshProjectComboBox->currentLayer() )
205         mInformationTextBrowser->setHtml( mMeshProjectComboBox->currentLayer()->htmlMetadata() );
206     }
207 
208     if ( mMeshFileRadioButton->isChecked() )
209     {
210       if ( mSourceMeshFromFile )
211         mInformationTextBrowser->setHtml( mSourceMeshFromFile->htmlMetadata() );
212     }
213 
214     mInformationTextBrowser->setOpenLinks( false );
215   }
216 };
217 
apply()218 bool QgsNewMeshLayerDialog::apply()
219 {
220   bool result = false;
221   const QString fileName = mFileWidget->filePath();
222   const QString format = mFormatComboBox->currentData().toString();
223 
224   QgsMesh mesh;
225   QgsCoordinateReferenceSystem crs;
226 
227   QgsMeshLayer *source = nullptr;
228 
229   if ( !mInitializeMeshGroupBox->isChecked() )
230   {
231     crs = mProjectionSelectionWidget->crs();
232   }
233   else if ( mMeshProjectRadioButton->isChecked() )
234   {
235     source = qobject_cast<QgsMeshLayer *>( mMeshProjectComboBox->currentLayer() );
236   }
237   else if ( mMeshFromFileWidget )
238   {
239     source = mSourceMeshFromFile.get();
240   }
241 
242   if ( source )
243   {
244     crs = source->crs();
245     source->dataProvider()->populateMesh( &mesh );
246   }
247 
248   const QgsProviderMetadata *providerMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "mdal" ) );
249   if ( providerMetadata )
250   {
251     result = providerMetadata->createMeshData( mesh, fileName, format, crs );
252     if ( result )
253     {
254       QString layerName = mLayerNameLineEdit->text();
255       if ( layerName.isEmpty() )
256       {
257         layerName = fileName;
258         QFileInfo fileInfo( fileName );
259         layerName = fileInfo.baseName();
260       }
261       std::unique_ptr<QgsMeshLayer> newMeshLayer = std::make_unique<QgsMeshLayer>( fileName, layerName, QStringLiteral( "mdal" ) );
262 
263       if ( newMeshLayer->crs() != crs )
264         newMeshLayer->setCrs( crs );
265 
266       if ( newMeshLayer->isValid() )
267       {
268         mNewLayer = newMeshLayer.get();
269         QgsProject::instance()->addMapLayer( newMeshLayer.release(), true, true );
270         return true;
271       }
272     }
273   }
274 
275   QMessageBox::warning( this, windowTitle(), tr( "Unable to create a new mesh layer with format \"%1\"" ).arg( mFormatComboBox->currentText() ) );
276   return false;
277 }
278 
newLayer() const279 QgsMeshLayer *QgsNewMeshLayerDialog::newLayer() const
280 {
281   return mNewLayer;
282 }
283 
284