QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
268int QgsPointCloudAttributeStatisticsModel::columnCount( const QModelIndex & ) const
269{
270 return StDev + 1;
271}
272
273int QgsPointCloudAttributeStatisticsModel::rowCount( const QModelIndex & ) const
274{
275 return mAttributes.count();
276}
277
278QVariant QgsPointCloudAttributeStatisticsModel::data( const QModelIndex &index, int role ) const
279{
280 if ( index.row() < 0 || index.row() >= mAttributes.count() )
281 return QVariant();
282
283 const QgsPointCloudAttribute &attr = mAttributes.at( index.row() );
284 const QgsPointCloudStatistics stats = mLayer->statistics();
285
286 switch ( role )
287 {
288 case Qt::DisplayRole:
289 case Qt::ToolTipRole:
290 {
291 switch ( index.column() )
292 {
293 case Name:
294 return attr.name();
295
296 case Min:
297 return stats.minimum( attr.name() );
298 case Max:
299 return stats.maximum( attr.name() );
300 case Mean:
301 return stats.mean( attr.name() );
302 case StDev:
303 return stats.stDev( attr.name() );
304 }
305 return QVariant();
306 }
307
308 case Qt::TextAlignmentRole:
309 {
310 switch ( index.column() )
311 {
312 case Name:
313 return static_cast<Qt::Alignment::Int>( Qt::AlignLeft | Qt::AlignVCenter );
314
315 case Min:
316 case Max:
317 case Mean:
318 case StDev:
319 return static_cast<Qt::Alignment::Int>( Qt::AlignRight | Qt::AlignVCenter );
320 }
321 return QVariant();
322 }
323
324 case Qt::FontRole:
325 {
326 if ( index.column() == Name )
327 {
328 QFont f;
329 f.setBold( true );
330 return f;
331 }
332 return QVariant();
333 }
334
335 case Qt::DecorationRole:
336 if ( index.column() == Name )
338 else
339 return QVariant();
340
341 default:
342 return QVariant();
343 }
344}
345
346QVariant QgsPointCloudAttributeStatisticsModel::headerData( int section, Qt::Orientation orientation, int role ) const
347{
348 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
349 {
350 switch ( section )
351 {
352 case Name:
353 return tr( "Attribute" );
354
355 case Min:
356 return tr( "Minimum" );
357 case Max:
358 return tr( "Maximum" );
359 case Mean:
360 return tr( "Mean" );
361 case StDev:
362 return tr( "Standard Deviation" );
363 }
364 }
365 return QVariant();
366}
367
368
369//
370// QgsPointCloudClassificationStatisticsModel
371//
372QgsPointCloudClassificationStatisticsModel::QgsPointCloudClassificationStatisticsModel( QgsPointCloudLayer *layer, const QString &attribute, QObject *parent )
373 : QAbstractTableModel( parent )
374 , mLayer( layer )
375 , mAttribute( attribute )
376{
377 mClassifications = layer->statistics().classesOf( attribute );
378 std::sort( mClassifications.begin(), mClassifications.end(), []( int a, int b ) -> bool { return a < b; } );
379}
380
381int QgsPointCloudClassificationStatisticsModel::columnCount( const QModelIndex & ) const
382{
383 return Percent + 1;
384}
385
386int QgsPointCloudClassificationStatisticsModel::rowCount( const QModelIndex & ) const
387{
388 return mClassifications.count();
389}
390
391QVariant QgsPointCloudClassificationStatisticsModel::data( const QModelIndex &index, int role ) const
392{
393 if ( index.row() < 0 || index.row() >= mClassifications.count() )
394 return QVariant();
395
396 const QVariant classValue = mClassifications.at( index.row() );
397 const QgsPointCloudStatistics stats = mLayer->statistics();
398
399 switch ( role )
400 {
401 case Qt::DisplayRole:
402 case Qt::ToolTipRole:
403 {
404 switch ( index.column() )
405 {
406 case Value:
407 return classValue.toString();
408
409 case Classification:
410 return QgsPointCloudDataProvider::translatedLasClassificationCodes().value( classValue.toInt() );
411
412 case Count:
413 return stats.availableClasses( mAttribute ).value( classValue.toInt(), 0 );
414
415 case Percent:
416 {
417 qint64 pointCount = stats.sampledPointsCount();
418 return ( ( double ) stats.availableClasses( mAttribute ).value( classValue.toInt(), 0 ) ) / pointCount * 100;
419 }
420 }
421 return QVariant();
422 }
423
424 case Qt::TextAlignmentRole:
425 {
426 switch ( index.column() )
427 {
428 case Classification:
429 return QVariant( Qt::AlignLeft | Qt::AlignVCenter );
430
431 case Value:
432 case Count:
433 case Percent:
434 return QVariant( Qt::AlignRight | Qt::AlignVCenter );
435 }
436 return QVariant();
437 }
438
439 case Qt::FontRole:
440 {
441 if ( index.column() == Classification )
442 {
443 QFont f;
444 f.setBold( true );
445 return f;
446 }
447 return QVariant();
448 }
449
450 default:
451 return QVariant();
452 }
453}
454
455QVariant QgsPointCloudClassificationStatisticsModel::headerData( int section, Qt::Orientation orientation, int role ) const
456{
457 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
458 {
459 switch ( section )
460 {
461 case Value:
462 return QVariant();
463
464 case Classification:
465 return tr( "Classification" );
466 case Count:
467 return tr( "Count" );
468 case Percent:
469 return tr( "%" );
470 }
471 }
472 return QVariant();
473}
474
@ ExcludeByDefault
If set, the layer should not be included in legends by default, and must be manually added by a user.
Definition qgis.h:4699
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.