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