QGIS API Documentation 3.41.0-Master (3440c17df1d)
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#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 QWidget *parent, Qt::WindowFlags fl )
35 : QgsSubsetStringEditorInterface( parent, fl )
36 , mLayer( layer )
37{
38 setupUi( this );
40
41 setupGuiViews();
42 populateAttributes();
43
44 connect( lstAttributes->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsPointCloudQueryBuilder::lstAttributes_currentChanged );
45 connect( lstAttributes, &QListView::doubleClicked, this, &QgsPointCloudQueryBuilder::lstAttributes_doubleClicked );
46 connect( lstValues, &QListView::doubleClicked, this, &QgsPointCloudQueryBuilder::lstValues_doubleClicked );
47 connect( btnEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnEqual_clicked );
48 connect( btnLessThan, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnLessThan_clicked );
49 connect( btnGreaterThan, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnGreaterThan_clicked );
50 connect( btnIn, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnIn_clicked );
51 connect( btnNotIn, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnNotIn_clicked );
52 connect( btnLessEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnLessEqual_clicked );
53 connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnGreaterEqual_clicked );
54 connect( btnNotEqual, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnNotEqual_clicked );
55 connect( btnAnd, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnAnd_clicked );
56 connect( btnOr, &QPushButton::clicked, this, &QgsPointCloudQueryBuilder::btnOr_clicked );
57
58 QPushButton *pbn = new QPushButton( tr( "&Test" ) );
59 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
60 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::test );
61
62 pbn = new QPushButton( tr( "&Clear" ) );
63 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
64 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::clear );
65
66 pbn = new QPushButton( tr( "&Save…" ) );
67 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
68 pbn->setToolTip( tr( "Save query to QQF file" ) );
69 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::saveQuery );
70
71 pbn = new QPushButton( tr( "&Load…" ) );
72 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
73 pbn->setToolTip( tr( "Load query from QQF file" ) );
74 connect( pbn, &QAbstractButton::clicked, this, &QgsPointCloudQueryBuilder::loadQuery );
75
76 mOrigSubsetString = layer->subsetString();
77
78 lblDataUri->setText( tr( "Set provider filter on %1" ).arg( layer->name() ) );
79 mTxtSql->setText( mOrigSubsetString );
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( QLatin1String( "Classification" ), 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( QStringLiteral( "%1 " ).arg( mModelAttributes->data( index ).toString() ) );
171 mTxtSql->setFocus();
172}
173
174void QgsPointCloudQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
175{
176 mTxtSql->insertText( QStringLiteral( "%1 " ).arg( mModelValues->data( index, Qt::UserRole ).toString() ) );
177 mTxtSql->setFocus();
178}
179
180void QgsPointCloudQueryBuilder::btnEqual_clicked()
181{
182 mTxtSql->insertText( QStringLiteral( "= " ) );
183 mTxtSql->setFocus();
184}
185
186void QgsPointCloudQueryBuilder::btnLessThan_clicked()
187{
188 mTxtSql->insertText( QStringLiteral( "< " ) );
189 mTxtSql->setFocus();
190}
191
192void QgsPointCloudQueryBuilder::btnGreaterThan_clicked()
193{
194 mTxtSql->insertText( QStringLiteral( "> " ) );
195 mTxtSql->setFocus();
196}
197
198void QgsPointCloudQueryBuilder::btnIn_clicked()
199{
200 mTxtSql->insertText( QStringLiteral( "IN () " ) );
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( QStringLiteral( "NOT IN () " ) );
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( QStringLiteral( "<= " ) );
219 mTxtSql->setFocus();
220}
221
222void QgsPointCloudQueryBuilder::btnGreaterEqual_clicked()
223{
224 mTxtSql->insertText( QStringLiteral( ">= " ) );
225 mTxtSql->setFocus();
226}
227
228void QgsPointCloudQueryBuilder::btnNotEqual_clicked()
229{
230 mTxtSql->insertText( QStringLiteral( "!= " ) );
231 mTxtSql->setFocus();
232}
233
234void QgsPointCloudQueryBuilder::btnAnd_clicked()
235{
236 mTxtSql->insertText( QStringLiteral( "AND " ) );
237 mTxtSql->setFocus();
238}
239
240void QgsPointCloudQueryBuilder::btnOr_clicked()
241{
242 mTxtSql->insertText( QStringLiteral( "OR " ) );
243 mTxtSql->setFocus();
244}
245
247{
248 if ( mTxtSql->text() != mOrigSubsetString )
249 {
250 if ( !mLayer->setSubsetString( mTxtSql->text() ) )
251 {
252 QMessageBox::warning( this, tr( "Query Result" ), tr( "Error in query. The subset string could not be set." ) );
253 return;
254 }
255 }
256
257 QDialog::accept();
258}
259
261{
262 if ( mLayer->subsetString() != mOrigSubsetString )
263 mLayer->setSubsetString( mOrigSubsetString );
264
265 QDialog::reject();
266}
267
268void QgsPointCloudQueryBuilder::test()
269{
270 QgsPointCloudExpression expression( mTxtSql->text() );
271 if ( !expression.isValid() && !mTxtSql->text().isEmpty() )
272 {
273 QMessageBox::warning( this,
274 tr( "Query Result" ),
275 tr( "An error occurred while parsing the expression:\n%1" ).arg( expression.parserErrorString() ) );
276 }
277 else
278 {
279 const QSet<QString> attributes = expression.referencedAttributes();
280 int offset;
281 for ( const auto &attribute : attributes )
282 {
283 if ( mLayer->dataProvider() &&
284 !mLayer->dataProvider()->attributes().find( attribute, offset ) )
285 {
286 QMessageBox::warning( this,
287 tr( "Query Result" ),
288 tr( "\"%1\" not recognized as an available attribute." ).arg( attribute ) );
289 return;
290 }
291 }
292 mLayer->setSubsetString( mTxtSql->text() );
293 QMessageBox::information( this,
294 tr( "Query Result" ),
295 tr( "The expression was successfully parsed." ) );
296 }
297}
298
299void QgsPointCloudQueryBuilder::clear()
300{
301 mTxtSql->clear();
302 mLayer->setSubsetString( QString() );
303}
304
305void QgsPointCloudQueryBuilder::saveQuery()
306{
307 const bool ok = QgsQueryBuilder::saveQueryToFile( mTxtSql->text() );
308 Q_UNUSED( ok )
309}
310
311void QgsPointCloudQueryBuilder::loadQuery()
312{
313 QString subset;
315 {
316 mTxtSql->clear();
317 mTxtSql->insertText( subset );
318 }
319}
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:103
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:209
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.
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.