QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsprocessingpointcloudexpressionlineedit.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingpointcloudexpressionlineedit.cpp
3 ---------------------
4 begin : April 2023
5 copyright : (C) 2023 by Alexander Bruy
6 email : alexander dot bruy 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
19#include "qgsgui.h"
20#include "qgsapplication.h"
21#include "qgsfilterlineedit.h"
22#include "qgspointcloudlayer.h"
23#include "qgspointcloudexpression.h"
24
25#include <QHBoxLayout>
26#include <QToolButton>
27#include <QListView>
28#include <QPushButton>
29#include <QMessageBox>
30
32
33QgsProcessingPointCloudExpressionLineEdit::QgsProcessingPointCloudExpressionLineEdit( QWidget *parent )
34 : QWidget( parent )
35{
36 mLineEdit = new QgsFilterLineEdit();
37 mLineEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
38
39 mButton = new QToolButton();
40 mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
41 mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
42 connect( mButton, &QAbstractButton::clicked, this, &QgsProcessingPointCloudExpressionLineEdit::editExpression );
43
44 QHBoxLayout *layout = new QHBoxLayout();
45 layout->setContentsMargins( 0, 0, 0, 0 );
46 layout->addWidget( mLineEdit );
47 layout->addWidget( mButton );
48 setLayout( layout );
49
50 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
51 setFocusProxy( mLineEdit );
52 connect( mLineEdit, &QLineEdit::textChanged, this, static_cast < void ( QgsProcessingPointCloudExpressionLineEdit::* )( const QString & ) > ( &QgsProcessingPointCloudExpressionLineEdit::expressionEdited ) );
53
54 setExpression( expression() );
55}
56
57QgsProcessingPointCloudExpressionLineEdit::~QgsProcessingPointCloudExpressionLineEdit() = default;
58
59void QgsProcessingPointCloudExpressionLineEdit::setLayer( QgsPointCloudLayer *layer )
60{
61 mLayer = layer;
62}
63
64QgsPointCloudLayer *QgsProcessingPointCloudExpressionLineEdit::layer() const
65{
66 return mLayer;
67}
68
69QString QgsProcessingPointCloudExpressionLineEdit::expression() const
70{
71 if ( mLineEdit )
72 return mLineEdit->text();
73
74 return QString();
75}
76
77void QgsProcessingPointCloudExpressionLineEdit::setExpression( const QString &newExpression )
78{
79 if ( mLineEdit )
80 mLineEdit->setText( newExpression );
81}
82
83void QgsProcessingPointCloudExpressionLineEdit::editExpression()
84{
85 const QString currentExpression = expression();
86 QgsProcessingPointCloudExpressionDialog dlg( mLayer );
87 dlg.setExpression( currentExpression );
88
89 if ( dlg.exec() )
90 {
91 const QString newExpression = dlg.expression();
92 setExpression( newExpression );
93 }
94}
95
96void QgsProcessingPointCloudExpressionLineEdit::expressionEdited()
97{
98 emit expressionChanged( expression() );
99}
100
101void QgsProcessingPointCloudExpressionLineEdit::expressionEdited( const QString &expression )
102{
103 emit expressionChanged( expression );
104}
105
106
107QgsProcessingPointCloudExpressionDialog::QgsProcessingPointCloudExpressionDialog( QgsPointCloudLayer *layer, const QString &startExpression, QWidget *parent )
108 : QDialog( parent )
109 , mLayer( layer )
110 , mInitialText( startExpression )
111{
112 setupUi( this );
114
115 mModelAttributes = new QStandardItemModel();
116 mModelValues = new QStandardItemModel();
117 lstAttributes->setModel( mModelAttributes );
118 lstValues->setModel( mModelValues );
119
120 populateAttributes();
121
122 connect( lstAttributes->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsProcessingPointCloudExpressionDialog::lstAttributes_currentChanged );
123 connect( lstAttributes, &QListView::doubleClicked, this, &QgsProcessingPointCloudExpressionDialog::lstAttributes_doubleClicked );
124 connect( lstValues, &QListView::doubleClicked, this, &QgsProcessingPointCloudExpressionDialog::lstValues_doubleClicked );
125 connect( btnEqual, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnEqual_clicked );
126 connect( btnLessThan, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnLessThan_clicked );
127 connect( btnGreaterThan, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnGreaterThan_clicked );
128 connect( btnIn, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnIn_clicked );
129 connect( btnNotIn, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnNotIn_clicked );
130 connect( btnLessEqual, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnLessEqual_clicked );
131 connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnGreaterEqual_clicked );
132 connect( btnNotEqual, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnNotEqual_clicked );
133 connect( btnAnd, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnAnd_clicked );
134 connect( btnOr, &QPushButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::btnOr_clicked );
135
136 QPushButton *pbn = new QPushButton( tr( "&Test" ) );
137 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
138 connect( pbn, &QAbstractButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::test );
139
140 pbn = new QPushButton( tr( "&Clear" ) );
141 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
142 connect( pbn, &QAbstractButton::clicked, this, &QgsProcessingPointCloudExpressionDialog::clear );
143
144 mTxtSql->setText( mInitialText );
145}
146
147void QgsProcessingPointCloudExpressionDialog::setExpression( const QString &text )
148{
149 mTxtSql->setText( text );
150}
151
152QString QgsProcessingPointCloudExpressionDialog::expression()
153{
154 return mTxtSql->text();
155}
156
157void QgsProcessingPointCloudExpressionDialog::populateAttributes()
158{
159 if ( !mLayer )
160 {
161 return;
162 }
163
164 const QgsFields &fields = mLayer->dataProvider()->attributes().toFields();
165 mTxtSql->setFields( fields );
166 for ( int idx = 0; idx < fields.count(); ++idx )
167 {
168 QStandardItem *myItem = new QStandardItem( fields.at( idx ).displayNameWithAlias() );
169 mModelAttributes->insertRow( mModelAttributes->rowCount(), myItem );
170 }
171}
172
173void QgsProcessingPointCloudExpressionDialog::lstAttributes_currentChanged( const QModelIndex &current, const QModelIndex &previous )
174{
175 Q_UNUSED( previous )
176
177 mModelValues->clear();
178 const QString attribute = current.data().toString();
179 if ( attribute.compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
180 {
181 const QMap<int, QString> codes = QgsPointCloudDataProvider::translatedLasClassificationCodes();
182 for ( int i = 0; i <= 18; ++i )
183 {
184 QStandardItem *item = new QStandardItem( QString( "%1: %2" ).arg( i ).arg( codes.value( i ) ) );
185 item->setData( i, Qt::UserRole );
186 mModelValues->insertRow( mModelValues->rowCount(), item );
187 }
188 }
189 else
190 {
191 const QgsPointCloudStatistics stats = mLayer->statistics();
192 double value = stats.minimum( attribute );
193 QString valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
194 QStandardItem *item = new QStandardItem( tr( "Minimum: %1" ).arg( valueString ) );
195 item->setData( value, Qt::UserRole );
196 mModelValues->insertRow( mModelValues->rowCount(), item );
197
198 value = stats.maximum( attribute );
199 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
200 item = new QStandardItem( tr( "Maximum: %1" ).arg( valueString ) );
201 item->setData( value, Qt::UserRole );
202 mModelValues->insertRow( mModelValues->rowCount(), item );
203
204 value = stats.mean( attribute );
205 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
206 item = new QStandardItem( tr( "Mean: %1" ).arg( valueString ) );
207 item->setData( value, Qt::UserRole );
208 mModelValues->insertRow( mModelValues->rowCount(), item );
209
210 value = stats.stDev( attribute );
211 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
212 item = new QStandardItem( tr( "StdDev: %1" ).arg( valueString ) );
213 item->setData( value, Qt::UserRole );
214 mModelValues->insertRow( mModelValues->rowCount(), item );
215 }
216}
217
218void QgsProcessingPointCloudExpressionDialog::lstAttributes_doubleClicked( const QModelIndex &index )
219{
220 mTxtSql->insertText( QStringLiteral( "%1 " ).arg( mModelAttributes->data( index ).toString() ) );
221 mTxtSql->setFocus();
222}
223
224void QgsProcessingPointCloudExpressionDialog::lstValues_doubleClicked( const QModelIndex &index )
225{
226 mTxtSql->insertText( QStringLiteral( "%1 " ).arg( mModelValues->data( index, Qt::UserRole ).toString() ) );
227 mTxtSql->setFocus();
228}
229
230void QgsProcessingPointCloudExpressionDialog::btnEqual_clicked()
231{
232 mTxtSql->insertText( QStringLiteral( "= " ) );
233 mTxtSql->setFocus();
234}
235
236void QgsProcessingPointCloudExpressionDialog::btnLessThan_clicked()
237{
238 mTxtSql->insertText( QStringLiteral( "< " ) );
239 mTxtSql->setFocus();
240}
241
242void QgsProcessingPointCloudExpressionDialog::btnGreaterThan_clicked()
243{
244 mTxtSql->insertText( QStringLiteral( "> " ) );
245 mTxtSql->setFocus();
246}
247
248void QgsProcessingPointCloudExpressionDialog::btnIn_clicked()
249{
250 mTxtSql->insertText( QStringLiteral( "IN () " ) );
251 int i, j;
252 mTxtSql->getCursorPosition( &i, &j );
253 mTxtSql->setCursorPosition( i, j - 2 );
254 mTxtSql->setFocus();
255}
256
257void QgsProcessingPointCloudExpressionDialog::btnNotIn_clicked()
258{
259 mTxtSql->insertText( QStringLiteral( "NOT IN () " ) );
260 int i, j;
261 mTxtSql->getCursorPosition( &i, &j );
262 mTxtSql->setCursorPosition( i, j - 2 );
263 mTxtSql->setFocus();
264}
265
266void QgsProcessingPointCloudExpressionDialog::btnLessEqual_clicked()
267{
268 mTxtSql->insertText( QStringLiteral( "<= " ) );
269 mTxtSql->setFocus();
270}
271
272void QgsProcessingPointCloudExpressionDialog::btnGreaterEqual_clicked()
273{
274 mTxtSql->insertText( QStringLiteral( ">= " ) );
275 mTxtSql->setFocus();
276}
277
278void QgsProcessingPointCloudExpressionDialog::btnNotEqual_clicked()
279{
280 mTxtSql->insertText( QStringLiteral( "!= " ) );
281 mTxtSql->setFocus();
282}
283
284void QgsProcessingPointCloudExpressionDialog::btnAnd_clicked()
285{
286 mTxtSql->insertText( QStringLiteral( "AND " ) );
287 mTxtSql->setFocus();
288}
289
290void QgsProcessingPointCloudExpressionDialog::btnOr_clicked()
291{
292 mTxtSql->insertText( QStringLiteral( "OR " ) );
293 mTxtSql->setFocus();
294}
295
296void QgsProcessingPointCloudExpressionDialog::test()
297{
298 QgsPointCloudExpression expression( mTxtSql->text() );
299
300 if ( !expression.isValid() && !mTxtSql->text().isEmpty() )
301 {
302 QMessageBox::warning( this,
303 tr( "Query Result" ),
304 tr( "An error occurred while parsing the expression:\n%1" ).arg( expression.parserErrorString() ) );
305 }
306 else
307 {
308 const QSet<QString> attributes = expression.referencedAttributes();
309 int offset;
310 for ( const auto &attribute : attributes )
311 {
312 if ( mLayer && mLayer->dataProvider() &&
313 !mLayer->dataProvider()->attributes().find( attribute, offset ) )
314 {
315 QMessageBox::warning( this,
316 tr( "Query Result" ),
317 tr( "\"%1\" not recognized as an available attribute." ).arg( attribute ) );
318 return;
319 }
320 }
321 QMessageBox::information( this,
322 tr( "Query Result" ),
323 tr( "The expression was successfully parsed." ) );
324 }
325}
326
327void QgsProcessingPointCloudExpressionDialog::clear()
328{
329 mTxtSql->clear();
330}
331
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
QString displayNameWithAlias() const
Returns the name to use when displaying this field and adds the alias in parenthesis if it is defined...
Definition: qgsfield.cpp:96
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:194
static QMap< int, QString > translatedLasClassificationCodes()
Returns the map of LAS classification code to translated string value, corresponding to the ASPRS Sta...
Represents a map layer supporting display of point clouds.
Class used to store statistics of a point cloud dataset.
double maximum(const QString &attribute) const
Returns the maximum value for the attribute attribute If no matching statistic is available then NaN ...
double stDev(const QString &attribute) const
Returns the standard deviation value for the attribute attribute If no matching statistic is availabl...
double mean(const QString &attribute) const
Returns the mean value for the attribute attribute If no matching statistic is available then NaN wil...
double minimum(const QString &attribute) const
Returns the minimum value for the attribute attribute If no matching statistic is available then NaN ...