QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgspointcloudclassifiedrendererwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudclassifiedrendererwidget.cpp
3 ---------------------
4 begin : November 2020
5 copyright : (C) 2020 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "moc_qgspointcloudclassifiedrendererwidget.cpp"
21#include "qgspointcloudlayer.h"
23#include "qgsdoublevalidator.h"
24#include "qgsstyle.h"
25#include "qgsguiutils.h"
27#include "qgscolordialog.h"
28#include "qgsapplication.h"
31
32#include <QMimeData>
33#include <QInputDialog>
34
36
37QgsPointCloudClassifiedRendererModel::QgsPointCloudClassifiedRendererModel( QObject *parent )
38 : QAbstractItemModel( parent )
39 , mMimeFormat( QStringLiteral( "application/x-qgspointcloudclassifiedrenderermodel" ) )
40{
41}
42
43void QgsPointCloudClassifiedRendererModel::setRendererCategories( const QgsPointCloudCategoryList &categories )
44{
45 if ( !mCategories.empty() )
46 {
47 beginRemoveRows( QModelIndex(), 0, std::max< int >( mCategories.size() - 1, 0 ) );
48 mCategories.clear();
49 endRemoveRows();
50 }
51 if ( categories.size() > 0 )
52 {
53 beginInsertRows( QModelIndex(), 0, categories.size() - 1 );
54 mCategories = categories;
55 endInsertRows();
56 }
57}
58
59void QgsPointCloudClassifiedRendererModel::addCategory( const QgsPointCloudCategory &cat )
60{
61 const int idx = mCategories.size();
62 beginInsertRows( QModelIndex(), idx, idx );
63 mCategories.append( cat );
64 endInsertRows();
65
66 emit categoriesChanged();
67}
68
69QgsPointCloudCategory QgsPointCloudClassifiedRendererModel::category( const QModelIndex &index )
70{
71 const int row = index.row();
72 if ( row >= mCategories.size() )
73 {
74 return QgsPointCloudCategory();
75 }
76 return mCategories.at( row );
77}
78
79Qt::ItemFlags QgsPointCloudClassifiedRendererModel::flags( const QModelIndex &index ) const
80{
81 if ( !index.isValid() || mCategories.empty() )
82 {
83 return Qt::ItemIsDropEnabled;
84 }
85
86 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
87 if ( index.column() == 1 ||
88 index.column() == 2 ||
89 index.column() == 3 )
90 {
91 flags |= Qt::ItemIsEditable;
92 }
93 return flags;
94}
95
96Qt::DropActions QgsPointCloudClassifiedRendererModel::supportedDropActions() const
97{
98 return Qt::MoveAction;
99}
100
101QVariant QgsPointCloudClassifiedRendererModel::data( const QModelIndex &index, int role ) const
102{
103 if ( !index.isValid() || mCategories.empty() )
104 return QVariant();
105
106 const QgsPointCloudCategory category = mCategories.value( index.row() );
107
108 switch ( role )
109 {
110 case Qt::CheckStateRole:
111 {
112 if ( index.column() == 0 )
113 {
114 return category.renderState() ? Qt::Checked : Qt::Unchecked;
115 }
116 break;
117 }
118
119 case Qt::DisplayRole:
120 case Qt::ToolTipRole:
121 {
122 switch ( index.column() )
123 {
124 case 1:
125 return category.pointSize() > 0 ? QString::number( category.pointSize() ) : QString();
126 case 2:
127 return QString::number( category.value() );
128 case 3:
129 return category.label();
130 case 4:
131 const float value = mPercentages.value( category.value(), -1 );
132 QString str;
133 if ( value < 0 )
134 str = tr( "N/A" );
135 else if ( value != 0 && std::round( value * 10 ) < 1 )
136 str = QStringLiteral( "< " ) + QLocale().toString( 0.1, 'f', 1 );
137 else
138 str = QLocale().toString( mPercentages.value( category.value() ), 'f', 1 );
139 return str;
140 }
141 break;
142 }
143
144 case Qt::DecorationRole:
145 {
146 if ( index.column() == 0 )
147 {
148 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
149 QPixmap pix( iconSize, iconSize );
150 pix.fill( category.color() );
151 return QIcon( pix );
152 }
153 break;
154 }
155
156 case Qt::TextAlignmentRole:
157 {
158 if ( index.column() == 0 )
159 return static_cast<Qt::Alignment::Int>( Qt::AlignHCenter );
160 if ( index.column() == 4 )
161 return static_cast<Qt::Alignment::Int>( Qt::AlignRight );
162 return static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
163 }
164
165 case Qt::EditRole:
166 {
167 switch ( index.column() )
168 {
169 case 1:
170 return category.pointSize() > 0 ? QString::number( category.pointSize() ) : QString();
171 case 2:
172 return QString::number( category.value() );
173 case 3:
174 return category.label();
175 }
176 break;
177 }
178 }
179
180 return QVariant();
181}
182
183bool QgsPointCloudClassifiedRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
184{
185 if ( !index.isValid() )
186 return false;
187
188 if ( index.column() == 0 && role == Qt::CheckStateRole )
189 {
190 mCategories[ index.row() ].setRenderState( value == Qt::Checked );
191 emit dataChanged( index, index );
192 emit categoriesChanged();
193 return true;
194 }
195
196 if ( role != Qt::EditRole )
197 return false;
198
199 switch ( index.column() )
200 {
201 case 1: // point size
202 {
203 const double size = value.toDouble();
204 mCategories[ index.row() ].setPointSize( size );
205 break;
206 }
207 case 2: // value
208 {
209 const int val = value.toInt();
210 mCategories[ index.row() ].setValue( val );
211 break;
212 }
213 case 3: // label
214 {
215 mCategories[ index.row() ].setLabel( value.toString() );
216 break;
217 }
218 default:
219 return false;
220 }
221
222 emit dataChanged( index, index );
223 emit categoriesChanged();
224 return true;
225}
226
227QVariant QgsPointCloudClassifiedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
228{
229 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
230 {
231 QStringList lst;
232 lst << tr( "Color" ) << tr( "Size" ) << tr( "Value" ) << tr( "Legend" ) << tr( "Percentage" );
233 return lst.value( section );
234 }
235 return QVariant();
236}
237
238int QgsPointCloudClassifiedRendererModel::rowCount( const QModelIndex &parent ) const
239{
240 if ( parent.isValid() )
241 {
242 return 0;
243 }
244 return mCategories.size();
245}
246
247int QgsPointCloudClassifiedRendererModel::columnCount( const QModelIndex &index ) const
248{
249 Q_UNUSED( index )
250 return 5;
251}
252
253QModelIndex QgsPointCloudClassifiedRendererModel::index( int row, int column, const QModelIndex &parent ) const
254{
255 if ( hasIndex( row, column, parent ) )
256 {
257 return createIndex( row, column );
258 }
259 return QModelIndex();
260}
261
262QModelIndex QgsPointCloudClassifiedRendererModel::parent( const QModelIndex &index ) const
263{
264 Q_UNUSED( index )
265 return QModelIndex();
266}
267
268QStringList QgsPointCloudClassifiedRendererModel::mimeTypes() const
269{
270 QStringList types;
271 types << mMimeFormat;
272 return types;
273}
274
275QMimeData *QgsPointCloudClassifiedRendererModel::mimeData( const QModelIndexList &indexes ) const
276{
277 QMimeData *mimeData = new QMimeData();
278 QByteArray encodedData;
279
280 QDataStream stream( &encodedData, QIODevice::WriteOnly );
281
282 // Create list of rows
283 const auto constIndexes = indexes;
284 for ( const QModelIndex &index : constIndexes )
285 {
286 if ( !index.isValid() || index.column() != 0 )
287 continue;
288
289 stream << index.row();
290 }
291 mimeData->setData( mMimeFormat, encodedData );
292 return mimeData;
293}
294
295bool QgsPointCloudClassifiedRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
296{
297 Q_UNUSED( row )
298 Q_UNUSED( column )
299 if ( action != Qt::MoveAction )
300 return true;
301
302 if ( !data->hasFormat( mMimeFormat ) )
303 return false;
304
305 QByteArray encodedData = data->data( mMimeFormat );
306 QDataStream stream( &encodedData, QIODevice::ReadOnly );
307
308 QVector<int> rows;
309 while ( !stream.atEnd() )
310 {
311 int r;
312 stream >> r;
313 rows.append( r );
314 }
315
316 int to = parent.row();
317 // to is -1 if dragged outside items, i.e. below any item,
318 // then move to the last position
319 if ( to == -1 )
320 to = mCategories.size(); // out of rang ok, will be decreased
321 for ( int i = rows.size() - 1; i >= 0; i-- )
322 {
323 int t = to;
324 if ( rows[i] < t )
325 t--;
326
327 if ( !( rows[i] < 0 || rows[i] >= mCategories.size() || t < 0 || t >= mCategories.size() ) )
328 {
329 mCategories.move( rows[i], t );
330 }
331
332 // current moved under another, shift its index up
333 for ( int j = 0; j < i; j++ )
334 {
335 if ( to < rows[j] && rows[i] > rows[j] )
336 rows[j] += 1;
337 }
338 // removed under 'to' so the target shifted down
339 if ( rows[i] < to )
340 to--;
341 }
342 emit dataChanged( createIndex( 0, 0 ), createIndex( mCategories.size(), 0 ) );
343 emit categoriesChanged();
344 return false;
345}
346
347void QgsPointCloudClassifiedRendererModel::deleteRows( QList<int> rows )
348{
349 std::sort( rows.begin(), rows.end() ); // list might be unsorted, depending on how the user selected the rows
350 for ( int i = rows.size() - 1; i >= 0; i-- )
351 {
352 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
353 mCategories.removeAt( rows[i] );
354 endRemoveRows();
355 }
356 emit categoriesChanged();
357}
358
359void QgsPointCloudClassifiedRendererModel::removeAllRows()
360{
361 beginRemoveRows( QModelIndex(), 0, mCategories.size() - 1 );
362 mCategories.clear();
363 endRemoveRows();
364 emit categoriesChanged();
365}
366
367void QgsPointCloudClassifiedRendererModel::setCategoryColor( int row, const QColor &color )
368{
369 mCategories[row].setColor( color );
370 emit dataChanged( createIndex( row, 0 ), createIndex( row, 0 ) );
371 emit categoriesChanged();
372}
373
374void QgsPointCloudClassifiedRendererModel::setCategoryPointSize( int row, double size )
375{
376 mCategories[row].setPointSize( size );
377 emit dataChanged( createIndex( row, 0 ), createIndex( row, 0 ) );
378 emit categoriesChanged();
379}
380
381// ------------------------------ View style --------------------------------
382QgsPointCloudClassifiedRendererViewStyle::QgsPointCloudClassifiedRendererViewStyle( QWidget *parent )
383 : QgsProxyStyle( parent )
384{}
385
386void QgsPointCloudClassifiedRendererViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const
387{
388 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
389 {
390 QStyleOption opt( *option );
391 opt.rect.setLeft( 0 );
392 // draw always as line above, because we move item to that index
393 opt.rect.setHeight( 0 );
394 if ( widget )
395 opt.rect.setRight( widget->width() );
396 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
397 return;
398 }
399 QProxyStyle::drawPrimitive( element, option, painter, widget );
400}
401
402
403QgsPointCloudClassifiedRendererWidget::QgsPointCloudClassifiedRendererWidget( QgsPointCloudLayer *layer, QgsStyle *style )
404 : QgsPointCloudRendererWidget( layer, style )
405{
406 setupUi( this );
407
408 mAttributeComboBox->setAllowEmptyAttributeName( true );
410
411 mModel = new QgsPointCloudClassifiedRendererModel( this );
412
413 if ( layer )
414 {
415 mAttributeComboBox->setLayer( layer );
416
417 setFromRenderer( layer->renderer() );
418 }
419
420 viewCategories->setModel( mModel );
421 viewCategories->resizeColumnToContents( 0 );
422 viewCategories->resizeColumnToContents( 1 );
423 viewCategories->resizeColumnToContents( 2 );
424 viewCategories->resizeColumnToContents( 3 );
425
426 viewCategories->setStyle( new QgsPointCloudClassifiedRendererViewStyle( viewCategories ) );
427
428 connect( mAttributeComboBox, &QgsPointCloudAttributeComboBox::attributeChanged,
429 this, &QgsPointCloudClassifiedRendererWidget::attributeChanged );
430 connect( mModel, &QgsPointCloudClassifiedRendererModel::categoriesChanged, this, &QgsPointCloudClassifiedRendererWidget::emitWidgetChanged );
431
432 connect( viewCategories, &QAbstractItemView::doubleClicked, this, &QgsPointCloudClassifiedRendererWidget::categoriesDoubleClicked );
433 connect( btnAddCategories, &QAbstractButton::clicked, this, &QgsPointCloudClassifiedRendererWidget::addCategories );
434 connect( btnDeleteCategories, &QAbstractButton::clicked, this, &QgsPointCloudClassifiedRendererWidget::deleteCategories );
435 connect( btnDeleteAllCategories, &QAbstractButton::clicked, this, &QgsPointCloudClassifiedRendererWidget::deleteAllCategories );
436 connect( btnAddCategory, &QAbstractButton::clicked, this, &QgsPointCloudClassifiedRendererWidget::addCategory );
437
438 contextMenu = new QMenu( tr( "Options" ), this );
439 contextMenu->addAction( tr( "Change &Color…" ), this, &QgsPointCloudClassifiedRendererWidget::changeCategoryColor );
440 contextMenu->addAction( tr( "Change &Opacity…" ), this, &QgsPointCloudClassifiedRendererWidget::changeCategoryOpacity );
441 contextMenu->addAction( tr( "Change &Size…" ), this, &QgsPointCloudClassifiedRendererWidget::changeCategoryPointSize );
442
443 viewCategories->setContextMenuPolicy( Qt::CustomContextMenu );
444 viewCategories->setSelectionMode( QAbstractItemView::ExtendedSelection );
445 connect( viewCategories, &QTreeView::customContextMenuRequested, this, [ = ]( QPoint ) { contextMenu->exec( QCursor::pos() ); } );
446}
447
448QgsPointCloudRendererWidget *QgsPointCloudClassifiedRendererWidget::create( QgsPointCloudLayer *layer, QgsStyle *style, QgsPointCloudRenderer * )
449{
450 return new QgsPointCloudClassifiedRendererWidget( layer, style );
451}
452
453QgsPointCloudRenderer *QgsPointCloudClassifiedRendererWidget::renderer()
454{
455 if ( !mLayer )
456 {
457 return nullptr;
458 }
459
460 std::unique_ptr< QgsPointCloudClassifiedRenderer > renderer = std::make_unique< QgsPointCloudClassifiedRenderer >();
461 renderer->setAttribute( mAttributeComboBox->currentAttribute() );
462 renderer->setCategories( mModel->categories() );
463
464 return renderer.release();
465}
466
467QgsPointCloudCategoryList QgsPointCloudClassifiedRendererWidget::categoriesList()
468{
469 return mModel->categories();
470}
471
472QString QgsPointCloudClassifiedRendererWidget::attribute()
473{
474 return mAttributeComboBox->currentAttribute();
475}
476
477void QgsPointCloudClassifiedRendererWidget::attributeChanged()
478{
479 if ( mBlockChangedSignal )
480 return;
481
482 mBlockChangedSignal = true;
483 mModel->removeAllRows();
484 mBlockChangedSignal = false;
485 addCategories();
486}
487
488void QgsPointCloudClassifiedRendererWidget::emitWidgetChanged()
489{
490 if ( mBlockChangedSignal )
491 return;
492
493 updateCategoriesPercentages();
494 emit widgetChanged();
495}
496
497void QgsPointCloudClassifiedRendererWidget::categoriesDoubleClicked( const QModelIndex &idx )
498{
499 if ( idx.isValid() && idx.column() == 0 )
500 changeCategoryColor();
501}
502
503void QgsPointCloudClassifiedRendererWidget::addCategories()
504{
505 if ( !mLayer || !mLayer->dataProvider() )
506 return;
507
508
509 const QString currentAttribute = mAttributeComboBox->currentAttribute();
510 const QgsPointCloudStatistics stats = mLayer->statistics();
511
512 const QgsPointCloudCategoryList currentCategories = mModel->categories();
513
514 const bool isClassificationAttribute = ( 0 == currentAttribute.compare( QStringLiteral( "Classification" ), Qt::CaseInsensitive ) );
515 const bool isBooleanAttribute = ( 0 == currentAttribute.compare( QStringLiteral( "Synthetic" ), Qt::CaseInsensitive ) ||
516 0 == currentAttribute.compare( QStringLiteral( "KeyPoint" ), Qt::CaseInsensitive ) ||
517 0 == currentAttribute.compare( QStringLiteral( "Withheld" ), Qt::CaseInsensitive ) ||
518 0 == currentAttribute.compare( QStringLiteral( "Overlap" ), Qt::CaseInsensitive ) );
519
520 QList<int> providerCategories = stats.classesOf( currentAttribute );
521
522 // for 0/1 attributes we should always show both 0 and 1 categories, unless we have full stats
523 // so we can show categories that are actually available
524 if ( isBooleanAttribute &&
525 ( providerCategories.isEmpty() || stats.sampledPointsCount() < mLayer->pointCount() ) )
526 providerCategories = { 0, 1 };
527
528 const QgsPointCloudCategoryList defaultLayerCategories = isClassificationAttribute ? QgsPointCloudRendererRegistry::classificationAttributeCategories( mLayer ) : QgsPointCloudCategoryList();
529
530 mBlockChangedSignal = true;
531 for ( const int &providerCategory : std::as_const( providerCategories ) )
532 {
533 // does this category already exist?
534 bool found = false;
535 for ( const QgsPointCloudCategory &c : currentCategories )
536 {
537 if ( c.value() == providerCategory )
538 {
539 found = true;
540 break;
541 }
542 }
543
544 if ( found )
545 continue;
546
547 QgsPointCloudCategory category;
548 if ( isClassificationAttribute )
549 {
550 for ( const QgsPointCloudCategory &c : defaultLayerCategories )
551 {
552 if ( c.value() == providerCategory )
553 category = c;
554 }
555 }
556 else
557 {
558 category = QgsPointCloudCategory( providerCategory, QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor(), QString::number( providerCategory ) );
559 }
560 mModel->addCategory( category );
561 }
562 mBlockChangedSignal = false;
563 emitWidgetChanged();
564}
565
566void QgsPointCloudClassifiedRendererWidget::addCategory()
567{
568 if ( !mModel )
569 return;
570
571 const QgsPointCloudCategory cat( mModel->categories().size(), QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor(), QString(), true );
572 mModel->addCategory( cat );
573}
574
575void QgsPointCloudClassifiedRendererWidget::deleteCategories()
576{
577 const QList<int> categoryIndexes = selectedCategories();
578 mModel->deleteRows( categoryIndexes );
579}
580
581void QgsPointCloudClassifiedRendererWidget::deleteAllCategories()
582{
583 mModel->removeAllRows();
584}
585
586void QgsPointCloudClassifiedRendererWidget::setFromRenderer( const QgsPointCloudRenderer *r )
587{
588 mBlockChangedSignal = true;
589 if ( const QgsPointCloudClassifiedRenderer *classifiedRenderer = dynamic_cast< const QgsPointCloudClassifiedRenderer *>( r ) )
590 {
591 mModel->setRendererCategories( classifiedRenderer->categories() );
592 mAttributeComboBox->setAttribute( classifiedRenderer->attribute() );
593 }
594 else
595 {
596 initialize();
597 }
598 mBlockChangedSignal = false;
599 emitWidgetChanged();
600}
601
602void QgsPointCloudClassifiedRendererWidget::setFromCategories( QgsPointCloudCategoryList categories, const QString &attribute )
603{
604 mBlockChangedSignal = true;
605 mModel->setRendererCategories( categories );
606 if ( !attribute.isEmpty() )
607 {
608 mAttributeComboBox->setAttribute( attribute );
609 }
610 else
611 {
612 initialize();
613 }
614 mBlockChangedSignal = false;
615 emitWidgetChanged();
616}
617
618void QgsPointCloudClassifiedRendererWidget::initialize()
619{
620 if ( mAttributeComboBox->findText( QStringLiteral( "Classification" ) ) > -1 )
621 {
622 mAttributeComboBox->setAttribute( QStringLiteral( "Classification" ) );
623 }
624 else
625 {
626 mAttributeComboBox->setCurrentIndex( mAttributeComboBox->count() > 1 ? 1 : 0 );
627 }
628 mModel->removeAllRows();
629 addCategories();
630}
631
632void QgsPointCloudClassifiedRendererWidget::changeCategoryColor()
633{
634 const QList<int> categoryList = selectedCategories();
635 if ( categoryList.isEmpty() )
636 {
637 return;
638 }
639
640 const QgsPointCloudCategory category = mModel->categories().value( categoryList.first() );
641
643 if ( panel && panel->dockMode() )
644 {
646 colorWidget->setPanelTitle( categoryList.count() == 1 ? category.label() : tr( "Select Color" ) );
647 colorWidget->setAllowOpacity( true );
648 colorWidget->setPreviousColor( category.color() );
649
650 connect( colorWidget, &QgsCompoundColorWidget::currentColorChanged, this, [ = ]( const QColor & newColor )
651 {
652 for ( int row : categoryList )
653 {
654 mModel->setCategoryColor( row, newColor );
655 }
656 } );
657 panel->openPanel( colorWidget );
658 }
659 else
660 {
661 const QColor newColor = QgsColorDialog::getColor( category.color(), this, category.label(), true );
662 if ( newColor.isValid() )
663 {
664 for ( int row : categoryList )
665 {
666 mModel->setCategoryColor( row, newColor );
667 }
668 }
669 }
670}
671
672void QgsPointCloudClassifiedRendererWidget::changeCategoryOpacity()
673{
674 const QList<int> categoryList = selectedCategories();
675 if ( categoryList.isEmpty() )
676 {
677 return;
678 }
679
680 const double oldOpacity = mModel->categories().value( categoryList.first() ).color().alphaF() * 100.0;
681
682 bool ok;
683 const double opacity = QInputDialog::getDouble( this, tr( "Opacity" ), tr( "Change symbol opacity [%]" ), oldOpacity, 0.0, 100.0, 1, &ok );
684 if ( ok )
685 {
686 for ( int row : categoryList )
687 {
688 const QgsPointCloudCategory category = mModel->categories().value( row );
689 QColor color = category.color();
690 color.setAlphaF( opacity / 100.0 );
691 mModel->setCategoryColor( row, color );
692 }
693 }
694}
695
696void QgsPointCloudClassifiedRendererWidget::changeCategoryPointSize()
697{
698 const QList<int> categoryList = selectedCategories();
699 if ( categoryList.isEmpty() )
700 {
701 return;
702 }
703
704 const double oldSize = mModel->categories().value( categoryList.first() ).pointSize();
705
706 bool ok;
707 const double size = QInputDialog::getDouble( this, tr( "Point Size" ), tr( "Change point size (set to 0 to reset to default point size)" ), oldSize, 0.0, 42.0, 1, &ok );
708 if ( ok )
709 {
710 for ( int row : categoryList )
711 {
712 mModel->setCategoryPointSize( row, size );
713 }
714 }
715}
716
717QList<int> QgsPointCloudClassifiedRendererWidget::selectedCategories()
718{
719 QList<int> rows;
720 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
721 for ( const QModelIndex &r : selectedRows )
722 {
723 if ( r.isValid() )
724 {
725 rows.append( r.row() );
726 }
727 }
728 return rows;
729}
730
731int QgsPointCloudClassifiedRendererWidget::currentCategoryRow()
732{
733 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
734 if ( !idx.isValid() )
735 return -1;
736 return idx.row();
737}
738
739void QgsPointCloudClassifiedRendererWidget::updateCategoriesPercentages()
740{
741 QMap < int, float > percentages;
742
743 const QgsPointCloudStatistics stats = mLayer->statistics();
744 const QMap<int, int> classes = stats.availableClasses( attribute() );
745 const int pointCount = stats.sampledPointsCount();
746 const QgsPointCloudCategoryList currentCategories = mModel->categories();
747
748 // when the stats are 100% accurate, we are sure that missing classes have a 0% of points
749 const bool statsExact = stats.sampledPointsCount() == mLayer->pointCount();
750 for ( const QgsPointCloudCategory &category : currentCategories )
751 {
752 if ( classes.contains( category.value() ) || statsExact )
753 percentages.insert( category.value(), ( double ) classes.value( category.value() ) / pointCount * 100 );
754 }
755 mModel->updateCategoriesPercentages( percentages );
756}
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), bool allowOpacity=false)
Returns a color selection from a color dialog.
QColor fetchRandomStyleColor() const
Returns a random color for use with a new symbol style (e.g.
A custom QGIS widget for selecting a color, including options for selecting colors via hue wheel,...
@ LayoutVertical
Use a narrower, vertically stacked layout.
void currentColorChanged(const QColor &color)
Emitted when the dialog's color changes.
void setPreviousColor(const QColor &color)
Sets the color to show in an optional "previous color" section.
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
Base class for any widget that can be shown as a inline panel.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
bool dockMode()
Returns the dock mode state.
void attributeChanged(const QString &name)
Emitted when the currently selected attribute changes.
Represents an individual category (class) from a QgsPointCloudClassifiedRenderer.
int value() const
Returns the value corresponding to this category.
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
QColor color() const
Returns the color which will be used to render this category.
double pointSize() const
Returns the point size for this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
Renders point clouds by a classification attribute.
Represents a map layer supporting display of point clouds.
QgsPointCloudRenderer * renderer()
Returns the 2D renderer for the point cloud.
static QgsPointCloudCategoryList classificationAttributeCategories(const QgsPointCloudLayer *layer)
Returns a list of categories using the available Classification classes of a specified layer,...
Base class for point cloud 2D renderer settings widgets.
Abstract base class for 2d point cloud renderers.
Class used to store statistics of a point cloud dataset.
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.
int sampledPointsCount() const
Returns the number of points used to calculate the statistics.
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style,...
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define str(x)
Definition qgis.cpp:39
QList< QgsPointCloudCategory > QgsPointCloudCategoryList