QGIS API Documentation 3.43.0-Master (3ee7834ace6)
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 "moc_qgspointcloudquerybuilder.cpp"
17#include "qgssettings.h"
18#include "qgspointcloudlayer.h"
19#include "qgspointcloudexpression.h"
20#include "qgshelp.h"
21#include "qgsgui.h"
22#include "qgsquerybuilder.h"
23
24#include <QDomDocument>
25#include <QDomElement>
26#include <QFileDialog>
27#include <QListView>
28#include <QMessageBox>
29#include <QPushButton>
30#include <QTextStream>
31
32
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 lblDataUri->setText( tr( "Set provider filter on %1" ).arg( layer->name() ) );
76}
77
78void QgsPointCloudQueryBuilder::showEvent( QShowEvent *event )
79{
80 mTxtSql->setFocus();
81 QDialog::showEvent( event );
82}
83
84void QgsPointCloudQueryBuilder::setupGuiViews()
85{
86 //Initialize the models
87 mModelAttributes = new QStandardItemModel();
88 mModelValues = new QStandardItemModel();
89
90 // Modes
91 lstAttributes->setViewMode( QListView::ListMode );
92 lstValues->setViewMode( QListView::ListMode );
93 lstAttributes->setSelectionBehavior( QAbstractItemView::SelectRows );
94 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
95 lstAttributes->setEditTriggers( QAbstractItemView::NoEditTriggers );
96 lstValues->setEditTriggers( QAbstractItemView::NoEditTriggers );
97 // Performance tip since Qt 4.1
98 lstAttributes->setUniformItemSizes( true );
99 lstValues->setUniformItemSizes( true );
100 // Colored rows
101 lstAttributes->setAlternatingRowColors( true );
102 lstValues->setAlternatingRowColors( true );
103
104 lstAttributes->setModel( mModelAttributes );
105 lstValues->setModel( mModelValues );
106}
107
108void QgsPointCloudQueryBuilder::populateAttributes()
109{
110 const QgsFields &fields = mLayer->dataProvider()->attributes().toFields();
111 mTxtSql->setFields( fields );
112 for ( int idx = 0; idx < fields.count(); ++idx )
113 {
114 QStandardItem *myItem = new QStandardItem( fields.at( idx ).displayNameWithAlias() );
115 mModelAttributes->insertRow( mModelAttributes->rowCount(), myItem );
116 }
117}
118
119void QgsPointCloudQueryBuilder::lstAttributes_currentChanged( const QModelIndex &current, const QModelIndex &previous )
120{
121 Q_UNUSED( previous )
122
123 mModelValues->clear();
124 const QString attribute = current.data().toString();
125 if ( attribute.compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
126 {
127 const QMap<int, QString> codes = QgsPointCloudDataProvider::translatedLasClassificationCodes();
128 for ( int i = 0; i <= 18; ++i )
129 {
130 QStandardItem *item = new QStandardItem( QString( "%1: %2" ).arg( i ).arg( codes.value( i ) ) );
131 item->setData( i, Qt::UserRole );
132 mModelValues->insertRow( mModelValues->rowCount(), item );
133 }
134 }
135 else
136 {
137 const QgsPointCloudStatistics stats = mLayer->statistics();
138 double value = stats.minimum( attribute );
139 QString valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
140 QStandardItem *item = new QStandardItem( tr( "Minimum: %1" ).arg( valueString ) );
141 item->setData( value, Qt::UserRole );
142 mModelValues->insertRow( mModelValues->rowCount(), item );
143
144 value = stats.maximum( attribute );
145 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
146 item = new QStandardItem( tr( "Maximum: %1" ).arg( valueString ) );
147 item->setData( value, Qt::UserRole );
148 mModelValues->insertRow( mModelValues->rowCount(), item );
149
150 value = stats.mean( attribute );
151 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
152 item = new QStandardItem( tr( "Mean: %1" ).arg( valueString ) );
153 item->setData( value, Qt::UserRole );
154 mModelValues->insertRow( mModelValues->rowCount(), item );
155
156 value = stats.stDev( attribute );
157 valueString = std::isnan( value ) ? tr( "n/a" ) : QString::number( value );
158 item = new QStandardItem( tr( "StdDev: %1" ).arg( valueString ) );
159 item->setData( value, Qt::UserRole );
160 mModelValues->insertRow( mModelValues->rowCount(), item );
161 }
162}
163
164void QgsPointCloudQueryBuilder::lstAttributes_doubleClicked( const QModelIndex &index )
165{
166 mTxtSql->insertText( QStringLiteral( "%1 " ).arg( mModelAttributes->data( index ).toString() ) );
167 mTxtSql->setFocus();
168}
169
170void QgsPointCloudQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
171{
172 mTxtSql->insertText( QStringLiteral( "%1 " ).arg( mModelValues->data( index, Qt::UserRole ).toString() ) );
173 mTxtSql->setFocus();
174}
175
176void QgsPointCloudQueryBuilder::btnEqual_clicked()
177{
178 mTxtSql->insertText( QStringLiteral( "= " ) );
179 mTxtSql->setFocus();
180}
181
182void QgsPointCloudQueryBuilder::btnLessThan_clicked()
183{
184 mTxtSql->insertText( QStringLiteral( "< " ) );
185 mTxtSql->setFocus();
186}
187
188void QgsPointCloudQueryBuilder::btnGreaterThan_clicked()
189{
190 mTxtSql->insertText( QStringLiteral( "> " ) );
191 mTxtSql->setFocus();
192}
193
194void QgsPointCloudQueryBuilder::btnIn_clicked()
195{
196 mTxtSql->insertText( QStringLiteral( "IN () " ) );
197 int i, j;
198 mTxtSql->getCursorPosition( &i, &j );
199 mTxtSql->setCursorPosition( i, j - 2 );
200 mTxtSql->setFocus();
201}
202
203void QgsPointCloudQueryBuilder::btnNotIn_clicked()
204{
205 mTxtSql->insertText( QStringLiteral( "NOT IN () " ) );
206 int i, j;
207 mTxtSql->getCursorPosition( &i, &j );
208 mTxtSql->setCursorPosition( i, j - 2 );
209 mTxtSql->setFocus();
210}
211
212void QgsPointCloudQueryBuilder::btnLessEqual_clicked()
213{
214 mTxtSql->insertText( QStringLiteral( "<= " ) );
215 mTxtSql->setFocus();
216}
217
218void QgsPointCloudQueryBuilder::btnGreaterEqual_clicked()
219{
220 mTxtSql->insertText( QStringLiteral( ">= " ) );
221 mTxtSql->setFocus();
222}
223
224void QgsPointCloudQueryBuilder::btnNotEqual_clicked()
225{
226 mTxtSql->insertText( QStringLiteral( "!= " ) );
227 mTxtSql->setFocus();
228}
229
230void QgsPointCloudQueryBuilder::btnAnd_clicked()
231{
232 mTxtSql->insertText( QStringLiteral( "AND " ) );
233 mTxtSql->setFocus();
234}
235
236void QgsPointCloudQueryBuilder::btnOr_clicked()
237{
238 mTxtSql->insertText( QStringLiteral( "OR " ) );
239 mTxtSql->setFocus();
240}
241
243{
244 if ( !test( true ) )
245 return;
246
247 QDialog::accept();
248}
249
251{
252 mTxtSql->setText( mOrigSubsetString );
253
254 QDialog::reject();
255}
256
257bool QgsPointCloudQueryBuilder::test( bool skipConfirmation )
258{
259 QgsPointCloudExpression expression( mTxtSql->text() );
260 if ( !expression.isValid() && !mTxtSql->text().isEmpty() )
261 {
262 QMessageBox::warning( this, tr( "Query Result" ), tr( "An error occurred while parsing the expression:\n%1" ).arg( expression.parserErrorString() ) );
263 return false;
264 }
265 else
266 {
267 const QSet<QString> attributes = expression.referencedAttributes();
268 int offset;
269 for ( const auto &attribute : attributes )
270 {
271 if ( mLayer->dataProvider() && !mLayer->dataProvider()->attributes().find( attribute, offset ) )
272 {
273 QMessageBox::warning( this, tr( "Query Result" ), tr( "\"%1\" not recognized as an available attribute." ).arg( attribute ) );
274 return false;
275 }
276 }
277
278 if ( !skipConfirmation )
279 QMessageBox::information( this, tr( "Query Result" ), tr( "The expression was successfully parsed." ) );
280 }
281 return true;
282}
283
284void QgsPointCloudQueryBuilder::clear()
285{
286 mTxtSql->clear();
287}
288
289void QgsPointCloudQueryBuilder::saveQuery()
290{
291 const bool ok = QgsQueryBuilder::saveQueryToFile( mTxtSql->text() );
292 Q_UNUSED( ok )
293}
294
295void QgsPointCloudQueryBuilder::loadQuery()
296{
297 QString subset;
299 {
300 mTxtSql->clear();
301 mTxtSql->insertText( subset );
302 }
303}
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:104
Container of fields for a vector layer.
Definition qgsfields.h:46
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:210
QString name
Definition qgsmaplayer.h:80
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.
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.