QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgspointcloudquerybuilder.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudquerybuilder.cpp - Query builder for point cloud layers
3 -----------------------------
4 begin : March 2022
5 copyright : (C) 2022 by Stefanos Natsis
6 email : uclaros at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
16
17#include "qgsgui.h"
18#include "qgshelp.h"
19#include "qgspointcloudexpression.h"
20#include "qgspointcloudlayer.h"
21#include "qgsquerybuilder.h"
22#include "qgssettings.h"
23
24#include <QDomDocument>
25#include <QDomElement>
26#include <QFileDialog>
27#include <QListView>
28#include <QMessageBox>
29#include <QPushButton>
30#include <QString>
31#include <QTextStream>
32
33#include "moc_qgspointcloudquerybuilder.cpp"
34
35using namespace Qt::StringLiterals;
36
38 : QgsSubsetStringEditorInterface( parent, fl )
39 , mLayer( layer )
40{
41 setupUi( this );
43
44 setupGuiViews();
45 populateAttributes();
46
47 connect( lstAttributes->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsPointCloudQueryBuilder::lstAttributes_currentChanged );
48 connect( lstAttributes, &QListView::doubleClicked, this, &QgsPointCloudQueryBuilder::lstAttributes_doubleClicked );
49 connect( lstValues, &QListView::doubleClicked, this, &QgsPointCloudQueryBuilder::lstValues_doubleClicked );
50 connect( btnEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnEqual_clicked );
51 connect( btnLessThan, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnLessThan_clicked );
52 connect( btnGreaterThan, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnGreaterThan_clicked );
53 connect( btnIn, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnIn_clicked );
54 connect( btnNotIn, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnNotIn_clicked );
55 connect( btnLessEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnLessEqual_clicked );
56 connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnGreaterEqual_clicked );
57 connect( btnNotEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnNotEqual_clicked );
58 connect( btnAnd, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnAnd_clicked );
59 connect( btnOr, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnOr_clicked );
60
61 QPushButton *pbn = new QPushButton( tr( "&Test" ) );
62 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
63 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::test );
64
65 pbn = new QPushButton( tr( "&Clear" ) );
66 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
67 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::clear );
68
69 pbn = new QPushButton( tr( "&Save…" ) );
70 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
71 pbn->setToolTip( tr( "Save query to QQF file" ) );
72 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::saveQuery );
73
74 pbn = new QPushButton( tr( "&Load…" ) );
75 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
76 pbn->setToolTip( tr( "Load query from QQF file" ) );
77 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::loadQuery );
78
79 lblDataUri->setText( tr( "Set provider filter on %1" ).arg( layer->name() ) );
80}
81
82void QgsPointCloudQueryBuilder::showEvent( QShowEvent *event )
83{
84 mTxtSql->setFocus();
85 QDialog::showEvent( event );
86}
87
88void QgsPointCloudQueryBuilder::setupGuiViews()
89{
90 //Initialize the models
91 mModelAttributes = new QStandardItemModel();
92 mModelValues = new QStandardItemModel();
93
94 // Modes
95 lstAttributes->setViewMode( QListView::ListMode );
96 lstValues->setViewMode( QListView::ListMode );
97 lstAttributes->setSelectionBehavior( QAbstractItemView::SelectRows );
98 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
99 lstAttributes->setEditTriggers( QAbstractItemView::NoEditTriggers );
100 lstValues->setEditTriggers( QAbstractItemView::NoEditTriggers );
101 // Performance tip since Qt 4.1
102 lstAttributes->setUniformItemSizes( true );
103 lstValues->setUniformItemSizes( true );
104 // Colored rows
105 lstAttributes->setAlternatingRowColors( true );
106 lstValues->setAlternatingRowColors( true );
107
108 lstAttributes->setModel( mModelAttributes );
109 lstValues->setModel( mModelValues );
110}
111
112void QgsPointCloudQueryBuilder::populateAttributes()
113{
114 const QgsFields &fields = mLayer->dataProvider()->attributes().toFields();
115 mTxtSql->setFields( fields );
116 for ( int idx = 0; idx < fields.count(); ++idx )
117 {
118 QStandardItem *myItem = new QStandardItem( fields.at( idx ).displayNameWithAlias() );
119 mModelAttributes->insertRow( mModelAttributes->rowCount(), myItem );
120 }
121}
122
123void QgsPointCloudQueryBuilder::lstAttributes_currentChanged( const QModelIndex &current, const QModelIndex &previous )
124{
125 Q_UNUSED( previous )
126
127 mModelValues->clear();
128 const QString attribute = current.data().toString();
129 if ( attribute.compare( "Classification"_L1, Qt::CaseInsensitive ) == 0 )
130 {
131 const QMap<int, QString> codes = QgsPointCloudDataProvider::translatedLasClassificationCodes();
132 for ( int i = 0; i <= 18; ++i )
133 {
134 QStandardItem *item = new QStandardItem( QString( "%1: %2" ).arg( i ).arg( codes.value( i ) ) );
135 item->setData( i, Qt::UserRole );
136 mModelValues->insertRow( mModelValues->rowCount(), item );
137 }
138 }
139 else
140 {
141 const QgsPointCloudStatistics stats = mLayer->statistics();
142 double value = stats.minimum( attribute );
143 QString valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
144 QStandardItem *item = new QStandardItem( tr( "Minimum: %1" ).arg( valueString ) );
145 item->setData( value, Qt::UserRole );
146 mModelValues->insertRow( mModelValues->rowCount(), item );
147
148 value = stats.maximum( attribute );
149 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
150 item = new QStandardItem( tr( "Maximum: %1" ).arg( valueString ) );
151 item->setData( value, Qt::UserRole );
152 mModelValues->insertRow( mModelValues->rowCount(), item );
153
154 value = stats.mean( attribute );
155 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
156 item = new QStandardItem( tr( "Mean: %1" ).arg( valueString ) );
157 item->setData( value, Qt::UserRole );
158 mModelValues->insertRow( mModelValues->rowCount(), item );
159
160 value = stats.stDev( attribute );
161 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
162 item = new QStandardItem( tr( "StdDev: %1" ).arg( valueString ) );
163 item->setData( value, Qt::UserRole );
164 mModelValues->insertRow( mModelValues->rowCount(), item );
165 }
166}
167
168void QgsPointCloudQueryBuilder::lstAttributes_doubleClicked( const QModelIndex &index )
169{
170 mTxtSql->insertText( u"%1 "_s.arg( mModelAttributes->data( index ).toString() ) );
171 mTxtSql->setFocus();
172}
173
174void QgsPointCloudQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
175{
176 mTxtSql->insertText( u"%1 "_s.arg( mModelValues->data( index, Qt::UserRole ).toString() ) );
177 mTxtSql->setFocus();
178}
179
180void QgsPointCloudQueryBuilder::btnEqual_clicked()
181{
182 mTxtSql->insertText( u"= "_s );
183 mTxtSql->setFocus();
184}
185
186void QgsPointCloudQueryBuilder::btnLessThan_clicked()
187{
188 mTxtSql->insertText( u"< "_s );
189 mTxtSql->setFocus();
190}
191
192void QgsPointCloudQueryBuilder::btnGreaterThan_clicked()
193{
194 mTxtSql->insertText( u"> "_s );
195 mTxtSql->setFocus();
196}
197
198void QgsPointCloudQueryBuilder::btnIn_clicked()
199{
200 mTxtSql->insertText( u"IN () "_s );
201 int i, j;
202 mTxtSql->getCursorPosition( &i, &j );
203 mTxtSql->setCursorPosition( i, j - 2 );
204 mTxtSql->setFocus();
205}
206
207void QgsPointCloudQueryBuilder::btnNotIn_clicked()
208{
209 mTxtSql->insertText( u"NOT IN () "_s );
210 int i, j;
211 mTxtSql->getCursorPosition( &i, &j );
212 mTxtSql->setCursorPosition( i, j - 2 );
213 mTxtSql->setFocus();
214}
215
216void QgsPointCloudQueryBuilder::btnLessEqual_clicked()
217{
218 mTxtSql->insertText( u"<= "_s );
219 mTxtSql->setFocus();
220}
221
222void QgsPointCloudQueryBuilder::btnGreaterEqual_clicked()
223{
224 mTxtSql->insertText( u">= "_s );
225 mTxtSql->setFocus();
226}
227
228void QgsPointCloudQueryBuilder::btnNotEqual_clicked()
229{
230 mTxtSql->insertText( u"!= "_s );
231 mTxtSql->setFocus();
232}
233
234void QgsPointCloudQueryBuilder::btnAnd_clicked()
235{
236 mTxtSql->insertText( u"AND "_s );
237 mTxtSql->setFocus();
238}
239
240void QgsPointCloudQueryBuilder::btnOr_clicked()
241{
242 mTxtSql->insertText( u"OR "_s );
243 mTxtSql->setFocus();
244}
245
247{
248 if ( !test( true ) )
249 return;
250
251 QDialog::accept();
252}
253
255{
256 mTxtSql->setText( mOrigSubsetString );
257
258 QDialog::reject();
259}
260
261bool QgsPointCloudQueryBuilder::test( bool skipConfirmation )
262{
263 QgsPointCloudExpression expression( mTxtSql->text() );
264 if ( !expression.isValid() && !mTxtSql->text().isEmpty() )
265 {
266 QMessageBox::warning( this, tr( "Query Result" ), tr( "An error occurred while parsing the expression:\n%1" ).arg( expression.parserErrorString() ) );
267 return false;
268 }
269 else
270 {
271 const QSet<QString> attributes = expression.referencedAttributes();
272 int offset;
273 for ( const auto &attribute : attributes )
274 {
275 if ( mLayer->dataProvider() && !mLayer->dataProvider()->attributes().find( attribute, offset ) )
276 {
277 QMessageBox::warning( this, tr( "Query Result" ), tr( "\"%1\" not recognized as an available attribute." ).arg( attribute ) );
278 return false;
279 }
280 }
281
282 if ( !skipConfirmation )
283 QMessageBox::information( this, tr( "Query Result" ), tr( "The expression was successfully parsed." ) );
284 }
285 return true;
286}
287
288void QgsPointCloudQueryBuilder::clear()
289{
290 mTxtSql->clear();
291}
292
293void QgsPointCloudQueryBuilder::saveQuery()
294{
295 const bool ok = QgsQueryBuilder::saveQueryToFile( mTxtSql->text() );
296 Q_UNUSED( ok )
297}
298
299void QgsPointCloudQueryBuilder::loadQuery()
300{
301 QString subset;
303 {
304 mTxtSql->clear();
305 mTxtSql->insertText( subset );
306 }
307}
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:109
int count
Definition qgsfields.h:50
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
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:224
QString name
Definition qgsmaplayer.h:87
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.
QgsPointCloudQueryBuilder(QgsPointCloudLayer *layer, QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
This constructor is used when the query builder is called from the layer properties dialog.
void showEvent(QShowEvent *event) override
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 ...
static bool loadQueryFromFile(QString &subset)
Load query from the XML file.
static bool saveQueryToFile(const QString &subset)
Save query to the XML file.
QgsSubsetStringEditorInterface(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.