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