QGIS API Documentation 3.99.0-Master (7d2ca374f2d)
Loading...
Searching...
No Matches
qgspointcloudlayerproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerproperties.cpp
3 --------------------------------------
4 Date : October 2020
5 Copyright : (C) 2020 by Nyall Dawson
6 Email : nyall dot dawson 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 ***************************************************************************/
15
17
18#include "qgsapplication.h"
20#include "qgsgui.h"
21#include "qgshelp.h"
23#include "qgsmaplayerlegend.h"
26#include "qgsmetadatawidget.h"
28#include "qgspointcloudlayer.h"
30
31#include <QDesktopServices>
32#include <QFileDialog>
33#include <QMenu>
34#include <QMessageBox>
35#include <QString>
36#include <QUrl>
37
38#include "moc_qgspointcloudlayerproperties.cpp"
39
40using namespace Qt::StringLiterals;
41
42QgsPointCloudLayerProperties::QgsPointCloudLayerProperties( QgsPointCloudLayer *lyr, QgsMapCanvas *canvas, QgsMessageBar *, QWidget *parent, Qt::WindowFlags flags )
43 : QgsLayerPropertiesDialog( lyr, canvas, u"PointCloudLayerProperties"_s, parent, flags )
44 , mLayer( lyr )
45{
46 setupUi( this );
47
48 connect( this, &QDialog::accepted, this, &QgsPointCloudLayerProperties::apply );
49 connect( this, &QDialog::rejected, this, &QgsPointCloudLayerProperties::rollback );
50 connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsPointCloudLayerProperties::apply );
51 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsPointCloudLayerProperties::showHelp );
52 connect( pbnQueryBuilder, &QPushButton::clicked, this, &QgsPointCloudLayerProperties::pbnQueryBuilder_clicked );
53
54 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsPointCloudLayerProperties::crsChanged );
55
56 mScaleRangeWidget->setMapCanvas( mCanvas );
57 chkUseScaleDependentRendering->setChecked( lyr->hasScaleBasedVisibility() );
58 mScaleRangeWidget->setScaleRange( lyr->minimumScale(), lyr->maximumScale() );
59
60 // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states,
61 // switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left),
62 // and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
63 initOptionsBase( false );
64
65 mOptsPage_Information->setContentsMargins( 0, 0, 0, 0 );
66
67 QVBoxLayout *layout = new QVBoxLayout( metadataFrame );
68 layout->setContentsMargins( 0, 0, 0, 0 );
69 metadataFrame->setContentsMargins( 0, 0, 0, 0 );
70 mMetadataWidget = new QgsMetadataWidget( this, mLayer );
71 mMetadataWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
72 mMetadataWidget->setMapCanvas( mCanvas );
73 layout->addWidget( mMetadataWidget );
74 metadataFrame->setLayout( layout );
75 mOptsPage_Metadata->setContentsMargins( 0, 0, 0, 0 );
76
77 setMetadataWidget( mMetadataWidget, mOptsPage_Metadata );
78
79 // update based on lyr's current state
80 syncToLayer();
81 connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsPointCloudLayerProperties::syncToLayer );
82
83 QgsSettings settings;
84 if ( !settings.contains( u"/Windows/PointCloudLayerProperties/tab"_s ) )
85 {
86 settings.setValue( u"Windows/PointCloudLayerProperties/tab"_s, mOptStackedWidget->indexOf( mOptsPage_Information ) );
87 }
88
89 mBtnStyle = new QPushButton( tr( "Style" ) );
90 QMenu *menuStyle = new QMenu( this );
91 menuStyle->addAction( tr( "Load Style…" ), this, &QgsPointCloudLayerProperties::loadStyleFromFile );
92 menuStyle->addAction( tr( "Save Style…" ), this, &QgsPointCloudLayerProperties::saveStyleToFile );
93 menuStyle->addSeparator();
94 menuStyle->addAction( tr( "Save as Default" ), this, &QgsPointCloudLayerProperties::saveStyleAsDefault );
95 menuStyle->addAction( tr( "Restore Default" ), this, &QgsPointCloudLayerProperties::loadDefaultStyle );
96 mBtnStyle->setMenu( menuStyle );
97 connect( menuStyle, &QMenu::aboutToShow, this, &QgsPointCloudLayerProperties::aboutToShowStyleMenu );
98
99 buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
100
101 mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
102 QMenu *menuMetadata = new QMenu( this );
103 mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata…" ), this, &QgsPointCloudLayerProperties::loadMetadataFromFile );
104 mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata…" ), this, &QgsPointCloudLayerProperties::saveMetadataToFile );
105 menuMetadata->addSeparator();
106 menuMetadata->addAction( tr( "Save as Default" ), this, &QgsPointCloudLayerProperties::saveMetadataAsDefault );
107 menuMetadata->addAction( tr( "Restore Default" ), this, &QgsPointCloudLayerProperties::loadDefaultMetadata );
108
109 mBtnMetadata->setMenu( menuMetadata );
110 buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
111
112 //Add help page references
113 mOptsPage_Information->setProperty( "helpPage", u"working_with_point_clouds/point_clouds.html#information-properties"_s );
114 mOptsPage_Source->setProperty( "helpPage", u"working_with_point_clouds/point_clouds.html#source-properties"_s );
115 mOptsPage_Rendering->setProperty( "helpPage", u"working_with_point_clouds/point_clouds.html#rendering-properties"_s );
116 mOptsPage_Metadata->setProperty( "helpPage", u"working_with_point_clouds/point_clouds.html#metadata-properties"_s );
117 mOptsPage_Statistics->setProperty( "helpPage", u"working_with_point_clouds/point_clouds.html#statistics-properties"_s );
118
119 mStatisticsTableView->setModel( new QgsPointCloudAttributeStatisticsModel( mLayer, mStatisticsTableView ) );
120 mStatisticsTableView->verticalHeader()->hide();
121
122 mBackupCrs = mLayer->crs();
123
124 const QgsPointCloudStatistics stats = mLayer->statistics();
125
126 if ( !stats.classesOf( u"Classification"_s ).isEmpty() )
127 {
128 mClassificationStatisticsTableView->setModel( new QgsPointCloudClassificationStatisticsModel( mLayer, u"Classification"_s, mStatisticsTableView ) );
129 mClassificationStatisticsTableView->verticalHeader()->hide();
130 }
131 else
132 {
133 mClassificationStatsGroupBox->hide();
134 }
135
136 mStatisticsCalculationWarningLabel->setHidden( mLayer->statisticsCalculationState() != QgsPointCloudLayer::PointCloudStatisticsCalculationState::Calculated );
137
139 mStatisticsCalculationWarningLabel->setHidden( state != QgsPointCloudLayer::PointCloudStatisticsCalculationState::Calculated );
140 } );
141
142 initialize();
143}
144
146{
147 mLegendConfigEmbeddedWidget->applyToLayer();
148
149 mMetadataWidget->acceptMetadata();
150
151 mLayer->setName( mLayerOrigNameLineEdit->text() );
152
153 mLayer->setScaleBasedVisibility( chkUseScaleDependentRendering->isChecked() );
154 mLayer->setMinimumScale( mScaleRangeWidget->minimumScale() );
155 mLayer->setMaximumScale( mScaleRangeWidget->maximumScale() );
156
157 mLayer->setSubsetString( txtSubsetSQL->text() );
158
159 mBackupCrs = mLayer->crs();
160
161 // legend
162 if ( QgsMapLayerLegend *legend = mLayer->legend() )
163 {
164 legend->setFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault, !mIncludeByDefaultInLayoutLegendsCheck->isChecked() );
165 }
166
167 for ( QgsMapLayerConfigWidget *w : std::as_const( mConfigWidgets ) )
168 w->apply();
169
170 mLayer->triggerRepaint();
171}
172
174{
175 if ( mBackupCrs != mLayer->crs() )
176 mLayer->setCrs( mBackupCrs );
177
179}
180
182{
183 // populate the general information
184 mLayerOrigNameLineEdit->setText( mLayer->name() );
185
186 /*
187 * Information Tab
188 */
189 QString myStyle = QgsApplication::reportStyleSheet();
190 myStyle.append( u"body { margin: 10px; }\n "_s );
191 mInformationTextBrowser->clear();
192 mInformationTextBrowser->document()->setDefaultStyleSheet( myStyle );
193 mInformationTextBrowser->setHtml( mLayer->htmlMetadata() );
194 mInformationTextBrowser->setOpenLinks( false );
195 connect( mInformationTextBrowser, &QTextBrowser::anchorClicked, this, &QgsPointCloudLayerProperties::openUrl );
196
197 mCrsSelector->setCrs( mLayer->crs() );
198
199 mSubsetGroupBox->setEnabled( true );
200 txtSubsetSQL->setText( mLayer->subsetString() );
201 txtSubsetSQL->setReadOnly( true );
202 txtSubsetSQL->setCaretWidth( 0 );
203 txtSubsetSQL->setCaretLineVisible( false );
204 pbnQueryBuilder->setEnabled( mLayer->dataProvider() && mLayer->dataProvider()->supportsSubsetString() && !mLayer->isEditable() );
205
206 for ( QgsMapLayerConfigWidget *w : std::as_const( mConfigWidgets ) )
207 w->syncToLayer( mLayer );
208
209 mStatisticsCalculationWarningLabel->setHidden( mLayer->statisticsCalculationState() != QgsPointCloudLayer::PointCloudStatisticsCalculationState::Calculated );
210
211 // legend
212 mLegendConfigEmbeddedWidget->setLayer( mLayer );
213 mIncludeByDefaultInLayoutLegendsCheck->setChecked( mLayer->legend() && !mLayer->legend()->flags().testFlag( Qgis::MapLayerLegendFlag::ExcludeByDefault ) );
214}
215
216void QgsPointCloudLayerProperties::aboutToShowStyleMenu()
217{
218 QMenu *m = qobject_cast<QMenu *>( sender() );
219
221 // re-add style manager actions!
222 m->addSeparator();
224}
225
226void QgsPointCloudLayerProperties::showHelp()
227{
228 const QVariant helpPage = mOptionsStackedWidget->currentWidget()->property( "helpPage" );
229
230 if ( helpPage.isValid() )
231 {
232 QgsHelp::openHelp( helpPage.toString() );
233 }
234 else
235 {
236 QgsHelp::openHelp( u"working_with_point_clouds/point_clouds.html"_s );
237 }
238}
239
240void QgsPointCloudLayerProperties::pbnQueryBuilder_clicked()
241{
242 QgsPointCloudQueryBuilder qb { mLayer };
243 qb.setSubsetString( mLayer->subsetString() );
244 if ( qb.exec() )
245 {
246 txtSubsetSQL->setText( qb.subsetString() );
247 }
248}
249
250void QgsPointCloudLayerProperties::crsChanged( const QgsCoordinateReferenceSystem &crs )
251{
252 QgsDatumTransformDialog::run( crs, QgsProject::instance()->crs(), this, mCanvas, tr( "Select transformation for the layer" ) );
253 mLayer->setCrs( crs );
254 mMetadataWidget->crsChanged();
255}
256
258
259//
260// QgsPointCloudAttributeStatisticsModel
261//
262QgsPointCloudAttributeStatisticsModel::QgsPointCloudAttributeStatisticsModel( QgsPointCloudLayer *layer, QObject *parent )
263 : QAbstractTableModel( parent )
264 , mLayer( layer )
265 , mAttributes( layer->attributes() )
266{
267}
268
269int QgsPointCloudAttributeStatisticsModel::columnCount( const QModelIndex & ) const
270{
271 return StDev + 1;
272}
273
274int QgsPointCloudAttributeStatisticsModel::rowCount( const QModelIndex & ) const
275{
276 return mAttributes.count();
277}
278
279QVariant QgsPointCloudAttributeStatisticsModel::data( const QModelIndex &index, int role ) const
280{
281 if ( index.row() < 0 || index.row() >= mAttributes.count() )
282 return QVariant();
283
284 const QgsPointCloudAttribute &attr = mAttributes.at( index.row() );
285 const QgsPointCloudStatistics stats = mLayer->statistics();
286
287 switch ( role )
288 {
289 case Qt::DisplayRole:
290 case Qt::ToolTipRole:
291 {
292 switch ( index.column() )
293 {
294 case Name:
295 return attr.name();
296
297 case Min:
298 return stats.minimum( attr.name() );
299 case Max:
300 return stats.maximum( attr.name() );
301 case Mean:
302 return stats.mean( attr.name() );
303 case StDev:
304 return stats.stDev( attr.name() );
305 }
306 return QVariant();
307 }
308
309 case Qt::TextAlignmentRole:
310 {
311 switch ( index.column() )
312 {
313 case Name:
314 return static_cast<Qt::Alignment::Int>( Qt::AlignLeft | Qt::AlignVCenter );
315
316 case Min:
317 case Max:
318 case Mean:
319 case StDev:
320 return static_cast<Qt::Alignment::Int>( Qt::AlignRight | Qt::AlignVCenter );
321 }
322 return QVariant();
323 }
324
325 case Qt::FontRole:
326 {
327 if ( index.column() == Name )
328 {
329 QFont f;
330 f.setBold( true );
331 return f;
332 }
333 return QVariant();
334 }
335
336 case Qt::DecorationRole:
337 if ( index.column() == Name )
339 else
340 return QVariant();
341
342 default:
343 return QVariant();
344 }
345}
346
347QVariant QgsPointCloudAttributeStatisticsModel::headerData( int section, Qt::Orientation orientation, int role ) const
348{
349 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
350 {
351 switch ( section )
352 {
353 case Name:
354 return tr( "Attribute" );
355
356 case Min:
357 return tr( "Minimum" );
358 case Max:
359 return tr( "Maximum" );
360 case Mean:
361 return tr( "Mean" );
362 case StDev:
363 return tr( "Standard Deviation" );
364 }
365 }
366 return QVariant();
367}
368
369
370//
371// QgsPointCloudClassificationStatisticsModel
372//
373QgsPointCloudClassificationStatisticsModel::QgsPointCloudClassificationStatisticsModel( QgsPointCloudLayer *layer, const QString &attribute, QObject *parent )
374 : QAbstractTableModel( parent )
375 , mLayer( layer )
376 , mAttribute( attribute )
377{
378 mClassifications = layer->statistics().classesOf( attribute );
379 std::sort( mClassifications.begin(), mClassifications.end(), []( int a, int b ) -> bool { return a < b; } );
380}
381
382int QgsPointCloudClassificationStatisticsModel::columnCount( const QModelIndex & ) const
383{
384 return Percent + 1;
385}
386
387int QgsPointCloudClassificationStatisticsModel::rowCount( const QModelIndex & ) const
388{
389 return mClassifications.count();
390}
391
392QVariant QgsPointCloudClassificationStatisticsModel::data( const QModelIndex &index, int role ) const
393{
394 if ( index.row() < 0 || index.row() >= mClassifications.count() )
395 return QVariant();
396
397 const QVariant classValue = mClassifications.at( index.row() );
398 const QgsPointCloudStatistics stats = mLayer->statistics();
399
400 switch ( role )
401 {
402 case Qt::DisplayRole:
403 case Qt::ToolTipRole:
404 {
405 switch ( index.column() )
406 {
407 case Value:
408 return classValue.toString();
409
410 case Classification:
411 return QgsPointCloudDataProvider::translatedLasClassificationCodes().value( classValue.toInt() );
412
413 case Count:
414 return stats.availableClasses( mAttribute ).value( classValue.toInt(), 0 );
415
416 case Percent:
417 {
418 qint64 pointCount = stats.sampledPointsCount();
419 return ( ( double ) stats.availableClasses( mAttribute ).value( classValue.toInt(), 0 ) ) / pointCount * 100;
420 }
421 }
422 return QVariant();
423 }
424
425 case Qt::TextAlignmentRole:
426 {
427 switch ( index.column() )
428 {
429 case Classification:
430 return QVariant( Qt::AlignLeft | Qt::AlignVCenter );
431
432 case Value:
433 case Count:
434 case Percent:
435 return QVariant( Qt::AlignRight | Qt::AlignVCenter );
436 }
437 return QVariant();
438 }
439
440 case Qt::FontRole:
441 {
442 if ( index.column() == Classification )
443 {
444 QFont f;
445 f.setBold( true );
446 return f;
447 }
448 return QVariant();
449 }
450
451 default:
452 return QVariant();
453 }
454}
455
456QVariant QgsPointCloudClassificationStatisticsModel::headerData( int section, Qt::Orientation orientation, int role ) const
457{
458 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
459 {
460 switch ( section )
461 {
462 case Value:
463 return QVariant();
464
465 case Classification:
466 return tr( "Classification" );
467 case Count:
468 return tr( "Count" );
469 case Percent:
470 return tr( "%" );
471 }
472 }
473 return QVariant();
474}
475
@ ExcludeByDefault
If set, the layer should not be included in legends by default, and must be manually added by a user.
Definition qgis.h:4664
static QString reportStyleSheet(QgsApplication::StyleSheetType styleSheetType=QgsApplication::StyleSheetType::Qt)
Returns a css style sheet for reports, the styleSheetType argument determines what type of stylesheet...
Represents a coordinate reference system (CRS).
static bool run(const QgsCoordinateReferenceSystem &sourceCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr, const QString &windowTitle=QString())
Runs the dialog (if required) prompting for the desired transform to use from sourceCrs to destinatio...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:41
Base class for "layer properties" dialogs, containing common utilities for handling functionality in ...
void saveMetadataToFile()
Allows the user to save the layer's metadata as a file.
virtual void rollback()
Rolls back changes made to the layer.
void saveStyleAsDefault()
Saves the current layer style as the default for the layer.
QList< QgsMapLayerConfigWidget * > mConfigWidgets
Layer config widgets.
void loadDefaultStyle()
Reloads the default style for the layer.
virtual void apply()=0
Applies the dialog settings to the layer.
virtual void syncToLayer()=0
Resets the dialog to the current layer state.
void saveStyleToFile()
Allows the user to save the layer's style to a file.
QgsMapCanvas * mCanvas
Associated map canvas.
void loadDefaultMetadata()
Reloads the default layer metadata for the layer.
void loadMetadataFromFile()
Allows the user to load layer metadata from a file.
void loadStyleFromFile()
Allows the user to load layer style from a file.
void saveMetadataAsDefault()
Saves the current layer metadata as the default for the layer.
void openUrl(const QUrl &url)
Handles opening a url from the dialog.
Map canvas is a class for displaying all GIS data types on a canvas.
void removesExtraMenuSeparators(QMenu *m)
removes extra separators from the menu
void addStyleManagerActions(QMenu *m, QgsMapLayer *layer)
adds actions to the menu in accordance to the layer
static QgsMapLayerStyleGuiUtils * instance()
returns a singleton instance of this class
void currentStyleChanged(const QString &currentName)
Emitted when the current style has been changed.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
double minimumScale() const
Returns the minimum map scale (i.e.
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
double maximumScale() const
Returns the maximum map scale (i.e.
A bar for displaying non-blocking messages to the user.
A wizard to edit metadata on a map layer.
static QIcon iconForAttributeType(QgsPointCloudAttribute::DataType type)
Returns an icon corresponding to an attribute type.
Attribute for point cloud data pair of name and size in bytes.
QString name() const
Returns name of the attribute.
DataType type() const
Returns the data type.
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.
PointCloudStatisticsCalculationState
Point cloud statistics calculation task.
@ Calculated
The statistics calculation task is done and statistics are available.
void statisticsCalculationStateChanged(QgsPointCloudLayer::PointCloudStatisticsCalculationState state)
Emitted when statistics calculation state has changed.
const QgsPointCloudStatistics statistics() const
Returns the object containing statistics.
QString subsetString() const override
Returns the subset string entered in the dialog.
void setSubsetString(const QString &subsetString) override
Sets a subset string into the dialog.
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...
QMap< int, int > availableClasses(const QString &attribute) const
Returns a map containing the count of each class of the attribute attribute If no matching statistic ...
QList< int > classesOf(const QString &attribute) const
Returns a list of existing classes which are present for the specified attribute.
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 ...
int sampledPointsCount() const
Returns the number of points used to calculate the statistics.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
Stores settings for use within QGIS.
Definition qgssettings.h:68
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.