1 /***************************************************************************
2   qgsmeshtransformcoordinatesdockwidget.cpp - QgsMeshTransformCoordinatesDockWidget
3 
4  ---------------------
5  begin                : 26.8.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 
17 #include "qgsmeshtransformcoordinatesdockwidget.h"
18 
19 #include "qgsgui.h"
20 #include "qgsexpressioncontextutils.h"
21 #include "qgsmesheditor.h"
22 #include "qgsmeshlayer.h"
23 #include "qgsmeshadvancedediting.h"
24 #include "qgsproject.h"
25 #include "qgsguiutils.h"
26 #include "qgshelp.h"
27 #include "qgscoordinateutils.h"
28 #include "qgsapplication.h"
29 
QgsMeshTransformCoordinatesDockWidget(QWidget * parent)30 QgsMeshTransformCoordinatesDockWidget::QgsMeshTransformCoordinatesDockWidget( QWidget *parent ):
31   QgsDockWidget( parent )
32 {
33   setupUi( this );
34 
35   QgsGui::enableAutoGeometryRestore( this );
36 
37   setWindowTitle( tr( "Transform Mesh Vertices by Expression" ) );
38   mExpressionLineEdits << mExpressionEditX << mExpressionEditY << mExpressionEditZ;
39   mCheckBoxes << mCheckBoxX << mCheckBoxY << mCheckBoxZ;
40 
41   Q_ASSERT( mExpressionLineEdits.count() == mCheckBoxes.count() );
42 
43   for ( int i = 0; i < mExpressionLineEdits.count(); ++i )
44   {
45     mExpressionLineEdits.at( i )->registerExpressionContextGenerator( this );
46     mExpressionLineEdits.at( i )->setEnabled( mCheckBoxes.at( i )->isChecked() );
47     connect( mCheckBoxes.at( i ), &QCheckBox::toggled, mExpressionLineEdits.at( i ), &QWidget::setEnabled );
48 
49     connect( mExpressionLineEdits.at( i ), &QgsExpressionLineEdit::expressionChanged, this, &QgsMeshTransformCoordinatesDockWidget::updateButton );
50     connect( mCheckBoxes.at( i ), &QCheckBox::toggled, this, &QgsMeshTransformCoordinatesDockWidget::updateButton );
51   }
52 
53   connect( mButtonPreview, &QToolButton::clicked, this, &QgsMeshTransformCoordinatesDockWidget::calculate );
54   connect( mButtonApply, &QPushButton::clicked, this, &QgsMeshTransformCoordinatesDockWidget::apply );
55   connect( mButtonImport, &QToolButton::toggled, this, &QgsMeshTransformCoordinatesDockWidget::onImportVertexClicked );
56 }
57 
createExpressionContext() const58 QgsExpressionContext QgsMeshTransformCoordinatesDockWidget::createExpressionContext() const
59 {
60   return QgsExpressionContext( {QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Vertex )} );
61 }
62 
transformedVertex(int i)63 QgsMeshVertex QgsMeshTransformCoordinatesDockWidget::transformedVertex( int i )
64 {
65   if ( ! mInputLayer || !mIsCalculated )
66     return QgsMeshVertex();
67 
68   return mTransformVertices.transformedVertex( mInputLayer, i );
69 }
70 
isResultValid() const71 bool QgsMeshTransformCoordinatesDockWidget::isResultValid() const
72 {
73   return mIsResultValid;
74 }
75 
isCalculated() const76 bool QgsMeshTransformCoordinatesDockWidget::isCalculated() const
77 {
78   return mIsCalculated;
79 }
80 
setInput(QgsMeshLayer * layer,const QList<int> & vertexIndexes)81 void QgsMeshTransformCoordinatesDockWidget::setInput( QgsMeshLayer *layer, const QList<int> &vertexIndexes )
82 {
83   mInputLayer = layer;
84   mInputVertices = vertexIndexes;
85   mIsCalculated = false;
86   mIsResultValid = false;
87   if ( !mInputLayer )
88     mLabelInformation->setText( tr( "No active mesh layer" ) );
89   else
90   {
91     if ( !mInputLayer->isEditable() )
92       mLabelInformation->setText( tr( "Mesh layer \"%1\" not in edit mode" ).arg( mInputLayer->name() ) );
93     else
94     {
95       if ( mInputVertices.count() == 0 )
96         mLabelInformation->setText( tr( "No vertex selected for mesh \"%1\"" ).arg( mInputLayer->name() ) );
97       else if ( mInputVertices.count() == 1 )
98         mLabelInformation->setText( tr( "1 vertex of mesh layer \"%1\" to transform" ).arg( mInputLayer->name() ) );
99       else
100         mLabelInformation->setText( tr( "%1 vertices of mesh layer \"%2\" to transform" ).
101                                     arg( QString::number( mInputVertices.count() ), mInputLayer->name() ) );
102     }
103   }
104   importVertexCoordinates();
105   updateButton();
106   emit calculationUpdated();
107 }
108 
calculate()109 void QgsMeshTransformCoordinatesDockWidget::calculate()
110 {
111   if ( !mInputLayer || mInputVertices.isEmpty() )
112     return;
113 
114   QgsTemporaryCursorOverride busyCursor( Qt::WaitCursor );
115   mTransformVertices.clear();
116   mTransformVertices.setInputVertices( mInputVertices );
117   mTransformVertices.setExpressions( mCheckBoxX->isChecked() ? mExpressionEditX->expression() : QString(),
118                                      mCheckBoxY->isChecked() ? mExpressionEditY->expression() : QString(),
119                                      mCheckBoxZ->isChecked() ? mExpressionEditZ->expression() : QString() );
120   QgsExpressionContext context;
121   context.appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
122 
123   mIsResultValid = mTransformVertices.calculate( mInputLayer );
124 
125   mIsCalculated = true;
126   mButtonApply->setEnabled( mIsResultValid );
127 
128   emit calculationUpdated();
129 }
130 
updateButton()131 void QgsMeshTransformCoordinatesDockWidget::updateButton()
132 {
133   mButtonApply->setEnabled( false );
134   bool isCalculable = mInputLayer && !mInputVertices.isEmpty();
135   if ( isCalculable )
136   {
137     isCalculable = false;
138     for ( const QCheckBox *cb : std::as_const( mCheckBoxes ) )
139       isCalculable |= cb->isChecked();
140 
141     if ( isCalculable )
142     {
143       for ( int i = 0; i < mCheckBoxes.count(); ++i )
144       {
145         bool checked = mCheckBoxes.at( i )->isChecked();
146         isCalculable &= !checked || mExpressionLineEdits.at( i )->isValidExpression();
147       }
148     }
149   }
150 
151   mButtonPreview->setEnabled( isCalculable );
152 }
153 
apply()154 void QgsMeshTransformCoordinatesDockWidget::apply()
155 {
156   emit aboutToBeApplied();
157   QgsTemporaryCursorOverride busyCursor( Qt::WaitCursor );
158   if ( mIsResultValid && mInputLayer && mInputLayer->meshEditor() )
159     mInputLayer->meshEditor()->advancedEdit( & mTransformVertices );
160   emit applied();
161 }
162 
onImportVertexClicked(bool checked)163 void QgsMeshTransformCoordinatesDockWidget::onImportVertexClicked( bool checked )
164 {
165   if ( checked )
166     importVertexCoordinates();
167   else
168   {
169     mExpressionEditX->setExpression( QString() );
170     mExpressionEditY->setExpression( QString() );
171     mExpressionEditZ->setExpression( QString() );
172   }
173 }
174 
175 
displayCoordinateText(const QgsCoordinateReferenceSystem & crs,double value)176 QString QgsMeshTransformCoordinatesDockWidget::displayCoordinateText( const QgsCoordinateReferenceSystem &crs, double value )
177 {
178   return QString::number( value, 'f', QgsCoordinateUtils::calculateCoordinatePrecisionForCrs( crs, QgsProject::instance() ) );
179 }
180 
importVertexCoordinates()181 void QgsMeshTransformCoordinatesDockWidget::importVertexCoordinates()
182 {
183   if ( mButtonImport->isChecked() && mInputLayer )
184   {
185     if ( mInputVertices.count() == 1 )
186     {
187       mExpressionEditX->setExpression( displayCoordinateText( mInputLayer->crs(), mInputLayer->nativeMesh()->vertex( mInputVertices.first() ).x() ) );
188       mExpressionEditY->setExpression( displayCoordinateText( mInputLayer->crs(), mInputLayer->nativeMesh()->vertex( mInputVertices.first() ).y() ) );
189       mExpressionEditZ->setExpression( displayCoordinateText( mInputLayer->crs(), mInputLayer->nativeMesh()->vertex( mInputVertices.first() ).z() ) );
190     }
191     else
192     {
193       mExpressionEditX->setExpression( QString() );
194       mExpressionEditY->setExpression( QString() );
195       mExpressionEditZ->setExpression( QString() );
196     }
197   }
198 }
199 
200