1 /***************************************************************************
2 qgsmeshcalculatordialog.cpp
3 ---------------------------
4 begin : January 2019
5 copyright : (C) 2018 by Peter Petrik
6 email : zilolv at gmail dot com
7 ***************************************************************************/
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
17 #include "qgsgdalutils.h"
18 #include "qgsmeshcalculatordialog.h"
19 #include "qgsproject.h"
20 #include "qgsmeshcalcnode.h"
21 #include "qgsmeshdataprovider.h"
22 #include "qgsproviderregistry.h"
23 #include "qgsmeshlayer.h"
24 #include "qgssettings.h"
25 #include "qgsgui.h"
26 #include "qgsvectorlayer.h"
27 #include "qgsmaplayerproxymodel.h"
28 #include "qgswkbtypes.h"
29 #include "qgsfeatureiterator.h"
30 #include "qgsmeshdatasetgrouptreeview.h"
31 #include "qgshelp.h"
32
33 #include "cpl_string.h"
34 #include "gdal.h"
35 #include "qgis.h"
36
37 #include <QMessageBox>
38 #include <QFileDialog>
39 #include <QFontDatabase>
40 #include <QMap>
41
QgsMeshCalculatorDialog(QgsMeshLayer * meshLayer,QWidget * parent,Qt::WindowFlags f)42 QgsMeshCalculatorDialog::QgsMeshCalculatorDialog( QgsMeshLayer *meshLayer, QWidget *parent, Qt::WindowFlags f )
43 : QDialog( parent, f ),
44 mLayer( meshLayer )
45 {
46 setupUi( this );
47 QgsGui::enableAutoGeometryRestore( this );
48
49 cboLayerMask->setFilters( QgsMapLayerProxyModel::PolygonLayer );
50 QgsMeshDatasetGroupListModel *model = new QgsMeshDatasetGroupListModel( this );
51 model->syncToLayer( meshLayer );
52 model->setDisplayProviderName( true );
53 mDatasetsListWidget->setModel( model );
54 mVariableNames = model->variableNames();
55
56 getMeshDrivers();
57 populateDriversComboBox( );
58 connect( mOutputFormatComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsMeshCalculatorDialog::updateInfoMessage );
59 connect( mOutputFormatComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsMeshCalculatorDialog::onOutputFormatChange );
60 connect( mOutputGroupNameLineEdit, &QLineEdit::textChanged, this, &QgsMeshCalculatorDialog::updateInfoMessage );
61
62 connect( mDatasetsListWidget, &QListView::doubleClicked, this, &QgsMeshCalculatorDialog::datasetGroupEntry );
63 connect( mCurrentLayerExtentButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mCurrentLayerExtentButton_clicked );
64 connect( mAllTimesButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mAllTimesButton_clicked );
65 connect( mExpressionTextEdit, &QTextEdit::textChanged, this, &QgsMeshCalculatorDialog::updateInfoMessage );
66
67 connect( useMaskCb, &QRadioButton::toggled, this, &QgsMeshCalculatorDialog::toggleExtendMask );
68 maskBox->setVisible( false );
69 useMaskCb->setEnabled( cboLayerMask->count() );
70
71 mXMaxSpinBox->setShowClearButton( false );
72 mXMinSpinBox->setShowClearButton( false );
73 mYMaxSpinBox->setShowClearButton( false );
74 mYMinSpinBox->setShowClearButton( false );
75
76 connect( mPlusPushButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mPlusPushButton_clicked );
77 connect( mMinusPushButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mMinusPushButton_clicked );
78 connect( mLessButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mLessButton_clicked );
79 connect( mLesserEqualButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mLesserEqualButton_clicked );
80 connect( mMultiplyPushButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mMultiplyPushButton_clicked );
81 connect( mDividePushButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mDividePushButton_clicked );
82 connect( mGreaterButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mGreaterButton_clicked );
83 connect( mGreaterEqualButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mGreaterEqualButton_clicked );
84 connect( mOpenBracketPushButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mOpenBracketPushButton_clicked );
85 connect( mCloseBracketPushButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mCloseBracketPushButton_clicked );
86 connect( mEqualButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mEqualButton_clicked );
87 connect( mNotEqualButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mNotEqualButton_clicked );
88 connect( mMinButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mMinButton_clicked );
89 connect( mMaxButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mMaxButton_clicked );
90 connect( mAbsButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mAbsButton_clicked );
91 connect( mPowButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mPowButton_clicked );
92 connect( mIfButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mIfButton_clicked );
93 connect( mAndButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mAndButton_clicked );
94 connect( mOrButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mOrButton_clicked );
95 connect( mNotButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mNotButton_clicked );
96 connect( mSumAggrButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mSumAggrButton_clicked );
97 connect( mMaxAggrButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mMaxAggrButton_clicked );
98 connect( mMinAggrButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mMinAggrButton_clicked );
99 connect( mAverageAggrButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mAverageAggrButton_clicked );
100 connect( mNoDataButton, &QPushButton::clicked, this, &QgsMeshCalculatorDialog::mNoDataButton_clicked );
101
102 mExpressionTextEdit->setCurrentFont( QFontDatabase::systemFont( QFontDatabase::FixedFont ) );
103
104 useFullLayerExtent();
105 repopulateTimeCombos();
106 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
107 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, [ = ]
108 {
109 QgsHelp::openHelp( QStringLiteral( "working_with_mesh/mesh_properties.html#mesh-calculator" ) );
110 } );
111
112 const QgsSettings settings;
113 mOutputDatasetFileWidget->setStorageMode( QgsFileWidget::SaveFile );
114 mOutputDatasetFileWidget->setDialogTitle( tr( "Enter Mesh Dataset File" ) );
115 mOutputDatasetFileWidget->setDefaultRoot( settings.value( QStringLiteral( "/MeshCalculator/lastOutputDir" ), QDir::homePath() ).toString() );
116 onOutputFormatChange();
117 connect( mOutputDatasetFileWidget, &QgsFileWidget::fileChanged, this, &QgsMeshCalculatorDialog::updateInfoMessage );
118
119 connect( mUseVirtualProviderCheckBox, &QCheckBox::clicked, this, &QgsMeshCalculatorDialog::onVirtualCheckboxChange );
120 onVirtualCheckboxChange();
121 }
122
123 QgsMeshCalculatorDialog::~QgsMeshCalculatorDialog() = default;
124
125
formulaString() const126 QString QgsMeshCalculatorDialog::formulaString() const
127 {
128 return mExpressionTextEdit->toPlainText();
129 }
130
meshLayer() const131 QgsMeshLayer *QgsMeshCalculatorDialog::meshLayer() const
132 {
133 return mLayer;
134 }
135
outputFile() const136 QString QgsMeshCalculatorDialog::outputFile() const
137 {
138 const QString ret = mOutputDatasetFileWidget->filePath();
139 return controlSuffix( ret );
140 }
141
outputExtent() const142 QgsRectangle QgsMeshCalculatorDialog::outputExtent() const
143 {
144 const QgsRectangle ret(
145 mXMinSpinBox->value(),
146 mYMinSpinBox->value(),
147 mXMaxSpinBox->value(),
148 mYMaxSpinBox->value()
149 );
150 return ret;
151 }
152
maskGeometry() const153 QgsGeometry QgsMeshCalculatorDialog::maskGeometry() const
154 {
155 QgsVectorLayer *mask_layer = qobject_cast<QgsVectorLayer *> ( cboLayerMask->currentLayer() );
156 if ( mask_layer )
157 {
158 return maskGeometry( mask_layer );
159 }
160 return QgsGeometry();
161 }
162
driver() const163 QString QgsMeshCalculatorDialog::driver() const
164 {
165 return mOutputFormatComboBox->currentData().toString();
166 }
167
groupName() const168 QString QgsMeshCalculatorDialog::groupName() const
169 {
170 return mOutputGroupNameLineEdit->text();
171 }
172
maskGeometry(QgsVectorLayer * layer) const173 QgsGeometry QgsMeshCalculatorDialog::maskGeometry( QgsVectorLayer *layer ) const
174 {
175 QgsFeatureIterator it = layer->getFeatures();
176 QVector<QgsGeometry> geometries;
177 QgsFeature feat;
178 while ( it.nextFeature( feat ) )
179 {
180 geometries.push_back( feat.geometry() );
181 }
182 const QgsGeometry ret = QgsGeometry::unaryUnion( geometries ) ;
183 return ret;
184 }
185
startTime() const186 double QgsMeshCalculatorDialog::startTime() const
187 {
188 if ( mStartTimeComboBox->currentIndex() > -1 )
189 return mStartTimeComboBox->itemData( mStartTimeComboBox->currentIndex() ).toDouble();
190 else
191 return 0;
192 }
193
endTime() const194 double QgsMeshCalculatorDialog::endTime() const
195 {
196 if ( mEndTimeComboBox->currentIndex() > -1 )
197 return mEndTimeComboBox->itemData( mEndTimeComboBox->currentIndex() ).toDouble();
198 else
199 return 0;
200 }
201
calculator() const202 std::unique_ptr<QgsMeshCalculator> QgsMeshCalculatorDialog::calculator() const
203 {
204 std::unique_ptr<QgsMeshCalculator> calc;
205 QgsMeshDatasetGroup::Type destination = QgsMeshDatasetGroup::Persistent;
206
207 if ( mUseVirtualProviderCheckBox->isChecked() )
208 destination = QgsMeshDatasetGroup::Virtual;
209
210 switch ( destination )
211 {
212 case QgsMeshDatasetGroup::Persistent:
213 if ( useExtentCb->isChecked() )
214 {
215 calc.reset(
216 new QgsMeshCalculator(
217 formulaString(),
218 driver(),
219 groupName(),
220 outputFile(),
221 outputExtent(),
222 startTime(),
223 endTime(),
224 meshLayer()
225 )
226 );
227 }
228 else
229 {
230 calc.reset(
231 new QgsMeshCalculator(
232 formulaString(),
233 driver(),
234 groupName(),
235 outputFile(),
236 maskGeometry(),
237 startTime(),
238 endTime(),
239 meshLayer()
240 )
241 );
242 }
243 break;
244 case QgsMeshDatasetGroup::Virtual:
245 if ( useExtentCb->isChecked() )
246 {
247 calc.reset(
248 new QgsMeshCalculator(
249 formulaString(),
250 groupName(),
251 outputExtent(),
252 destination,
253 meshLayer(),
254 startTime(),
255 endTime()
256 )
257 );
258 }
259 else
260 {
261 calc.reset(
262 new QgsMeshCalculator(
263 formulaString(),
264 groupName(),
265 maskGeometry(),
266 destination,
267 meshLayer(),
268 startTime(),
269 endTime()
270 )
271 );
272 }
273 break;
274 default:
275 break;
276 }
277
278 return calc;
279 }
280
datasetGroupEntry(const QModelIndex & index)281 void QgsMeshCalculatorDialog::datasetGroupEntry( const QModelIndex &index )
282 {
283 const QString group = quoteDatasetGroupEntry( datasetGroupName( index ) );
284 mExpressionTextEdit->insertPlainText( QStringLiteral( " %1 " ).arg( group ) );
285 }
286
toggleExtendMask()287 void QgsMeshCalculatorDialog::toggleExtendMask()
288 {
289 bool visible = useMaskCb->isChecked();
290 extendBox->setVisible( !visible );
291 maskBox->setVisible( visible );
292 }
293
updateInfoMessage()294 void QgsMeshCalculatorDialog::updateInfoMessage()
295 {
296 QgsMeshDriverMetadata::MeshDriverCapability requiredCapability;
297
298 // expression is valid
299 const QgsMeshCalculator::Result result = QgsMeshCalculator::expressionIsValid(
300 formulaString(),
301 meshLayer(),
302 requiredCapability
303 );
304 const bool expressionValid = result == QgsMeshCalculator::Success;
305
306 // selected driver is appropriate
307 const bool notInFile = mUseVirtualProviderCheckBox->isChecked();
308 bool driverValid = false;
309 if ( expressionValid )
310 {
311 const QString driverKey = driver();
312 if ( mMeshDrivers.contains( driverKey ) )
313 {
314 const QgsMeshDriverMetadata meta = mMeshDrivers[driverKey];
315 driverValid = meta.capabilities().testFlag( requiredCapability );
316 }
317 }
318 else
319 {
320 // can't determine if driver is valid when expression is invalid
321 driverValid = true;
322 }
323
324 // output path is selected
325 bool filePathValid = false;
326 QString outputPath = outputFile();
327 if ( !outputPath.isEmpty() )
328 {
329 outputPath = QFileInfo( outputPath ).absolutePath();
330 filePathValid = QFileInfo( outputPath ).isWritable();
331 }
332
333 // group name
334 const bool groupNameValid = !groupName().isEmpty() && !mVariableNames.contains( groupName() );
335
336 if ( expressionValid &&
337 ( notInFile || ( driverValid && filePathValid ) ) &&
338 groupNameValid )
339 {
340 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( true );
341 mExpressionValidLabel->setText( tr( "Expression valid" ) );
342 }
343 else
344 {
345 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
346 if ( !expressionValid )
347 mExpressionValidLabel->setText( tr( "Expression invalid" ) );
348 else if ( !filePathValid && !notInFile )
349 mExpressionValidLabel->setText( tr( "Invalid file path" ) );
350 else if ( !driverValid && !notInFile )
351 mExpressionValidLabel->setText( tr( "Selected driver cannot store data defined on %1" ).arg( requiredCapability == QgsMeshDriverMetadata::CanWriteFaceDatasets ? tr( " faces " ) : tr( " vertices " ) ) );
352 else if ( !groupNameValid )
353 mExpressionValidLabel->setText( tr( "Invalid group name" ) );
354 }
355 }
356
onVirtualCheckboxChange()357 void QgsMeshCalculatorDialog::onVirtualCheckboxChange()
358 {
359 mOutputDatasetFileWidget->setVisible( !mUseVirtualProviderCheckBox->isChecked() );
360 mOutputDatasetFileLabel->setVisible( !mUseVirtualProviderCheckBox->isChecked() );
361 mOutputFormatComboBox->setVisible( !mUseVirtualProviderCheckBox->isChecked() );
362 mOutputFormatLabel->setVisible( !mUseVirtualProviderCheckBox->isChecked() );
363 updateInfoMessage();
364 }
365
onOutputFormatChange()366 void QgsMeshCalculatorDialog::onOutputFormatChange()
367 {
368 const QString suffix = currentOutputSuffix();
369 if ( !suffix.isEmpty() )
370 {
371 QString filter = mOutputFormatComboBox->currentText();
372 filter.append( QStringLiteral( " (*.%1)" ).arg( suffix ) );
373 mOutputDatasetFileWidget->setFilter( filter );
374 }
375 }
376
datasetGroupName(const QModelIndex & index) const377 QString QgsMeshCalculatorDialog::datasetGroupName( const QModelIndex &index ) const
378 {
379 if ( !index.isValid() )
380 return QString();
381
382 return index.data( Qt::DisplayRole ).toString();
383 }
384
mCurrentLayerExtentButton_clicked()385 void QgsMeshCalculatorDialog::mCurrentLayerExtentButton_clicked()
386 {
387 useFullLayerExtent();
388 }
389
mAllTimesButton_clicked()390 void QgsMeshCalculatorDialog::mAllTimesButton_clicked()
391 {
392 useAllTimesFromLayer();
393 }
394
mPlusPushButton_clicked()395 void QgsMeshCalculatorDialog::mPlusPushButton_clicked()
396 {
397 mExpressionTextEdit->insertPlainText( QStringLiteral( " + " ) );
398 }
399
mMinusPushButton_clicked()400 void QgsMeshCalculatorDialog::mMinusPushButton_clicked()
401 {
402 mExpressionTextEdit->insertPlainText( QStringLiteral( " - " ) );
403 }
404
mLessButton_clicked()405 void QgsMeshCalculatorDialog::mLessButton_clicked()
406 {
407 mExpressionTextEdit->insertPlainText( QStringLiteral( " < " ) );
408 }
409
mLesserEqualButton_clicked()410 void QgsMeshCalculatorDialog::mLesserEqualButton_clicked()
411 {
412 mExpressionTextEdit->insertPlainText( QStringLiteral( " <= " ) );
413 }
414
mMultiplyPushButton_clicked()415 void QgsMeshCalculatorDialog::mMultiplyPushButton_clicked()
416 {
417 mExpressionTextEdit->insertPlainText( QStringLiteral( " * " ) );
418 }
419
mDividePushButton_clicked()420 void QgsMeshCalculatorDialog::mDividePushButton_clicked()
421 {
422 mExpressionTextEdit->insertPlainText( QStringLiteral( " / " ) );
423 }
424
mGreaterButton_clicked()425 void QgsMeshCalculatorDialog::mGreaterButton_clicked()
426 {
427 mExpressionTextEdit->insertPlainText( QStringLiteral( " > " ) );
428 }
429
mGreaterEqualButton_clicked()430 void QgsMeshCalculatorDialog::mGreaterEqualButton_clicked()
431 {
432 mExpressionTextEdit->insertPlainText( QStringLiteral( " >= " ) );
433 }
434
mOpenBracketPushButton_clicked()435 void QgsMeshCalculatorDialog::mOpenBracketPushButton_clicked()
436 {
437 mExpressionTextEdit->insertPlainText( QStringLiteral( " ( " ) );
438 }
439
mCloseBracketPushButton_clicked()440 void QgsMeshCalculatorDialog::mCloseBracketPushButton_clicked()
441 {
442 mExpressionTextEdit->insertPlainText( QStringLiteral( " ) " ) );
443 }
444
mEqualButton_clicked()445 void QgsMeshCalculatorDialog::mEqualButton_clicked()
446 {
447 mExpressionTextEdit->insertPlainText( QStringLiteral( " = " ) );
448 }
449
mNotEqualButton_clicked()450 void QgsMeshCalculatorDialog::mNotEqualButton_clicked()
451 {
452 mExpressionTextEdit->insertPlainText( QStringLiteral( " != " ) );
453 }
454
mMinButton_clicked()455 void QgsMeshCalculatorDialog::mMinButton_clicked()
456 {
457 mExpressionTextEdit->insertPlainText( QStringLiteral( " min ( A , B ) " ) );
458 }
459
mMaxButton_clicked()460 void QgsMeshCalculatorDialog::mMaxButton_clicked()
461 {
462 mExpressionTextEdit->insertPlainText( QStringLiteral( " max ( A , B ) " ) );
463 }
464
mAbsButton_clicked()465 void QgsMeshCalculatorDialog::mAbsButton_clicked()
466 {
467 mExpressionTextEdit->insertPlainText( QStringLiteral( " abs ( " ) );
468 }
469
mPowButton_clicked()470 void QgsMeshCalculatorDialog::mPowButton_clicked()
471 {
472 mExpressionTextEdit->insertPlainText( QStringLiteral( " ^ " ) );
473 }
474
mIfButton_clicked()475 void QgsMeshCalculatorDialog::mIfButton_clicked()
476 {
477 mExpressionTextEdit->insertPlainText( QStringLiteral( " if ( 1 = 1 , NODATA , NODATA ) " ) );
478 }
479
mAndButton_clicked()480 void QgsMeshCalculatorDialog::mAndButton_clicked()
481 {
482 mExpressionTextEdit->insertPlainText( QStringLiteral( " and " ) );
483 }
484
mOrButton_clicked()485 void QgsMeshCalculatorDialog::mOrButton_clicked()
486 {
487 mExpressionTextEdit->insertPlainText( QStringLiteral( " or " ) );
488 }
489
mNotButton_clicked()490 void QgsMeshCalculatorDialog::mNotButton_clicked()
491 {
492 mExpressionTextEdit->insertPlainText( QStringLiteral( " not " ) );
493 }
494
mSumAggrButton_clicked()495 void QgsMeshCalculatorDialog::mSumAggrButton_clicked()
496 {
497 mExpressionTextEdit->insertPlainText( QStringLiteral( " sum_aggr ( " ) );
498 }
499
mMaxAggrButton_clicked()500 void QgsMeshCalculatorDialog::mMaxAggrButton_clicked()
501 {
502 mExpressionTextEdit->insertPlainText( QStringLiteral( " max_aggr ( " ) );
503 }
504
mMinAggrButton_clicked()505 void QgsMeshCalculatorDialog::mMinAggrButton_clicked()
506 {
507 mExpressionTextEdit->insertPlainText( QStringLiteral( " min_aggr ( " ) );
508 }
509
mAverageAggrButton_clicked()510 void QgsMeshCalculatorDialog::mAverageAggrButton_clicked()
511 {
512 mExpressionTextEdit->insertPlainText( QStringLiteral( " average_aggr ( " ) );
513 }
514
mNoDataButton_clicked()515 void QgsMeshCalculatorDialog::mNoDataButton_clicked()
516 {
517 mExpressionTextEdit->insertPlainText( QStringLiteral( " NODATA " ) );
518 }
519
quoteDatasetGroupEntry(const QString group)520 QString QgsMeshCalculatorDialog::quoteDatasetGroupEntry( const QString group )
521 {
522 QString ret( group );
523 ret = QStringLiteral( "\"%1\"" ).arg( ret.replace( "\"", "\\\"" ) );
524 return ret;
525 }
526
controlSuffix(const QString & fileName) const527 QString QgsMeshCalculatorDialog::controlSuffix( const QString &fileName ) const
528 {
529 if ( fileName.isEmpty() )
530 return fileName;
531
532 const QFileInfo fileInfo( fileName );
533
534 const QString appropriateSuffix = currentOutputSuffix();
535
536 const QString existingSuffix = fileInfo.suffix();
537 if ( !( existingSuffix.isEmpty() && appropriateSuffix.isEmpty() )
538 && existingSuffix != appropriateSuffix )
539 {
540 const int pos = fileName.lastIndexOf( '.' );
541 QString ret = fileName.left( pos + 1 );
542 ret.append( appropriateSuffix );
543
544 return ret;
545 }
546
547 return fileName;
548 }
549
currentOutputSuffix() const550 QString QgsMeshCalculatorDialog::currentOutputSuffix() const
551 {
552 const QString currentDriver = mOutputFormatComboBox->currentData().toString();
553 QString suffix;
554 if ( mMeshDrivers.contains( currentDriver ) )
555 suffix = mMeshDrivers[currentDriver].writeDatasetOnFileSuffix();
556
557 return suffix;
558 }
559
getMeshDrivers()560 void QgsMeshCalculatorDialog::getMeshDrivers()
561 {
562 QgsProviderMetadata *providerMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "mdal" ) );
563 if ( providerMetadata )
564 {
565 const QList<QgsMeshDriverMetadata> allDrivers = providerMetadata->meshDriversMetadata();
566 for ( const QgsMeshDriverMetadata &meta : allDrivers )
567 {
568 if ( meta.capabilities().testFlag( QgsMeshDriverMetadata::MeshDriverCapability::CanWriteFaceDatasets ) ||
569 meta.capabilities().testFlag( QgsMeshDriverMetadata::MeshDriverCapability::CanWriteEdgeDatasets ) ||
570 meta.capabilities().testFlag( QgsMeshDriverMetadata::MeshDriverCapability::CanWriteVertexDatasets ) )
571 mMeshDrivers[meta.name()] = meta;
572 }
573 }
574 }
575
populateDriversComboBox()576 void QgsMeshCalculatorDialog::populateDriversComboBox( )
577 {
578
579 whileBlocking( mOutputFormatComboBox )->clear();
580
581 const QList< QgsMeshDriverMetadata > vals = mMeshDrivers.values();
582 for ( const QgsMeshDriverMetadata &meta : vals )
583 {
584 whileBlocking( mOutputFormatComboBox )->addItem( meta.description(), meta.name() );
585 }
586 mOutputFormatComboBox->setCurrentIndex( 0 );
587 }
588
useFullLayerExtent()589 void QgsMeshCalculatorDialog::useFullLayerExtent()
590 {
591 QgsMeshLayer *layer = meshLayer();
592 if ( !layer )
593 return;
594
595 const QgsRectangle layerExtent = layer->extent();
596 mXMinSpinBox->setValue( layerExtent.xMinimum() );
597 mXMaxSpinBox->setValue( layerExtent.xMaximum() );
598 mYMinSpinBox->setValue( layerExtent.yMinimum() );
599 mYMaxSpinBox->setValue( layerExtent.yMaximum() );
600 }
601
useAllTimesFromLayer()602 void QgsMeshCalculatorDialog::useAllTimesFromLayer()
603 {
604 const QString datasetGroupName = currentDatasetGroup();
605 setTimesByDatasetGroupName( datasetGroupName );
606 }
607
currentDatasetGroup() const608 QString QgsMeshCalculatorDialog::currentDatasetGroup() const
609 {
610 const QModelIndex index = mDatasetsListWidget->currentIndex();
611
612 if ( !index.isValid() )
613 return QString();
614
615 return datasetGroupName( index );
616 }
617
setTimesByDatasetGroupName(const QString group)618 void QgsMeshCalculatorDialog::setTimesByDatasetGroupName( const QString group )
619 {
620 QgsMeshLayer *layer = meshLayer();
621 if ( !layer || !layer->dataProvider() )
622 return;
623 const QgsMeshDataProvider *dp = layer->dataProvider();
624
625 // find group index from group name
626 int groupIndex = -1;
627 for ( int i = 0; i < dp->datasetGroupCount(); ++i )
628 {
629 const QgsMeshDatasetGroupMetadata meta = dp->datasetGroupMetadata( i );
630 if ( meta.name() == group )
631 {
632 groupIndex = i;
633 break;
634 }
635 }
636
637 if ( groupIndex < 0 )
638 return; //not found
639
640 const int datasetCount = dp->datasetCount( groupIndex );
641 if ( datasetCount < 1 )
642 return; // group without datasets
643
644
645 // find maximum and minimum time in this group
646 const double minTime = dp->datasetMetadata( QgsMeshDatasetIndex( groupIndex, 0 ) ).time();
647 int idx = mStartTimeComboBox->findData( minTime );
648 if ( idx >= 0 )
649 mStartTimeComboBox->setCurrentIndex( idx );
650
651 const double maxTime = dp->datasetMetadata( QgsMeshDatasetIndex( groupIndex, datasetCount - 1 ) ).time();
652 idx = mEndTimeComboBox->findData( maxTime );
653 if ( idx >= 0 )
654 mEndTimeComboBox->setCurrentIndex( idx );
655 }
656
repopulateTimeCombos()657 void QgsMeshCalculatorDialog::repopulateTimeCombos()
658 {
659 QgsMeshLayer *layer = meshLayer();
660 if ( !layer || !layer->dataProvider() )
661 return;
662 const QgsMeshDataProvider *dp = layer->dataProvider();
663
664 // extract all times from all datasets
665 QMap<QString, double> times;
666
667 for ( int groupIndex = 0; groupIndex < dp->datasetGroupCount(); ++groupIndex )
668 {
669 for ( int datasetIndex = 0; datasetIndex < dp->datasetCount( groupIndex ); ++datasetIndex )
670 {
671 const QgsMeshDatasetMetadata meta = dp->datasetMetadata( QgsMeshDatasetIndex( groupIndex, datasetIndex ) );
672 const double time = meta.time();
673 const QString timestr = layer->formatTime( time );
674
675 times[timestr] = time;
676 }
677 }
678
679 // sort by text
680 auto keys = times.keys();
681 keys.sort();
682
683 mStartTimeComboBox->blockSignals( true );
684 mEndTimeComboBox->blockSignals( true );
685 mStartTimeComboBox->clear();
686 mEndTimeComboBox->clear();
687
688 // populate combos
689 for ( const QString &key : keys )
690 {
691 mStartTimeComboBox->addItem( key, times[key] );
692 mEndTimeComboBox->addItem( key, times[key] );
693 }
694
695 mStartTimeComboBox->blockSignals( false );
696 mEndTimeComboBox->blockSignals( false );
697
698
699 if ( !times.empty() )
700 {
701 mStartTimeComboBox->setCurrentIndex( 0 );
702 mEndTimeComboBox->setCurrentIndex( times.size() - 1 );
703 }
704 }
705