35 QgsPointCloudClassifiedRendererModel::QgsPointCloudClassifiedRendererModel( QObject *parent )
36 : QAbstractItemModel( parent )
37 , mMimeFormat( QStringLiteral(
"application/x-qgspointcloudclassifiedrenderermodel" ) )
43 if ( !mCategories.empty() )
45 beginRemoveRows( QModelIndex(), 0, std::max< int >( mCategories.size() - 1, 0 ) );
49 if ( categories.size() > 0 )
51 beginInsertRows( QModelIndex(), 0, categories.size() - 1 );
52 mCategories = categories;
59 const int idx = mCategories.size();
60 beginInsertRows( QModelIndex(), idx, idx );
61 mCategories.append( cat );
64 emit categoriesChanged();
69 const int row = index.row();
70 if ( row >= mCategories.size() )
74 return mCategories.at( row );
77 Qt::ItemFlags QgsPointCloudClassifiedRendererModel::flags(
const QModelIndex &index )
const
79 if ( !index.isValid() || mCategories.empty() )
81 return Qt::ItemIsDropEnabled;
84 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
85 if ( index.column() == 1 )
87 flags |= Qt::ItemIsEditable;
89 else if ( index.column() == 2 )
91 flags |= Qt::ItemIsEditable;
96 Qt::DropActions QgsPointCloudClassifiedRendererModel::supportedDropActions()
const
98 return Qt::MoveAction;
101 QVariant QgsPointCloudClassifiedRendererModel::data(
const QModelIndex &index,
int role )
const
103 if ( !index.isValid() || mCategories.empty() )
110 case Qt::CheckStateRole:
112 if ( index.column() == 0 )
114 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
119 case Qt::DisplayRole:
120 case Qt::ToolTipRole:
122 switch ( index.column() )
126 return QString::number( category.
value() );
129 return category.
label();
131 const float value = mPercentages.value( category.
value(), -1 );
135 else if ( value != 0 && std::round( value * 10 ) < 1 )
136 str = QStringLiteral(
"< " ) + QLocale().toString( 0.1,
'f', 1 );
138 str = QLocale().toString( mPercentages.value( category.
value() ),
'f', 1 );
144 case Qt::DecorationRole:
146 if ( index.column() == 0 )
150 pix.fill( category.
color() );
156 case Qt::TextAlignmentRole:
158 if ( index.column() == 0 )
159 return static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter );
160 if ( index.column() == 3 )
161 return static_cast<Qt::Alignment::Int
>( Qt::AlignRight );
162 return static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
167 switch ( index.column() )
171 return QString::number( category.
value() );
175 return category.
label();
184 bool QgsPointCloudClassifiedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
186 if ( !index.isValid() )
189 if ( index.column() == 0 && role == Qt::CheckStateRole )
191 mCategories[ index.row() ].setRenderState( value == Qt::Checked );
192 emit dataChanged( index, index );
193 emit categoriesChanged();
197 if ( role != Qt::EditRole )
200 switch ( index.column() )
204 const int val = value.toInt();
205 mCategories[ index.row() ].setValue( val );
210 mCategories[ index.row() ].setLabel( value.toString() );
217 emit dataChanged( index, index );
218 emit categoriesChanged();
222 QVariant QgsPointCloudClassifiedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
224 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 4 )
227 lst << tr(
"Color" ) << tr(
"Value" ) << tr(
"Legend" ) << tr(
"Percentage" );
228 return lst.value( section );
233 int QgsPointCloudClassifiedRendererModel::rowCount(
const QModelIndex &parent )
const
235 if ( parent.isValid() )
239 return mCategories.size();
242 int QgsPointCloudClassifiedRendererModel::columnCount(
const QModelIndex &index )
const
248 QModelIndex QgsPointCloudClassifiedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
250 if ( hasIndex( row, column, parent ) )
252 return createIndex( row, column );
254 return QModelIndex();
257 QModelIndex QgsPointCloudClassifiedRendererModel::parent(
const QModelIndex &index )
const
260 return QModelIndex();
263 QStringList QgsPointCloudClassifiedRendererModel::mimeTypes()
const
266 types << mMimeFormat;
270 QMimeData *QgsPointCloudClassifiedRendererModel::mimeData(
const QModelIndexList &indexes )
const
272 QMimeData *mimeData =
new QMimeData();
273 QByteArray encodedData;
275 QDataStream stream( &encodedData, QIODevice::WriteOnly );
278 const auto constIndexes = indexes;
279 for (
const QModelIndex &index : constIndexes )
281 if ( !index.isValid() || index.column() != 0 )
284 stream << index.row();
286 mimeData->setData( mMimeFormat, encodedData );
290 bool QgsPointCloudClassifiedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
294 if ( action != Qt::MoveAction )
297 if ( !data->hasFormat( mMimeFormat ) )
300 QByteArray encodedData = data->data( mMimeFormat );
301 QDataStream stream( &encodedData, QIODevice::ReadOnly );
304 while ( !stream.atEnd() )
311 int to = parent.row();
315 to = mCategories.size();
316 for (
int i = rows.size() - 1; i >= 0; i-- )
322 if ( !( rows[i] < 0 || rows[i] >= mCategories.size() || t < 0 || t >= mCategories.size() ) )
324 mCategories.move( rows[i], t );
328 for (
int j = 0; j < i; j++ )
330 if ( to < rows[j] && rows[i] > rows[j] )
337 emit dataChanged( createIndex( 0, 0 ), createIndex( mCategories.size(), 0 ) );
338 emit categoriesChanged();
342 void QgsPointCloudClassifiedRendererModel::deleteRows( QList<int> rows )
344 std::sort( rows.begin(), rows.end() );
345 for (
int i = rows.size() - 1; i >= 0; i-- )
347 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
348 mCategories.removeAt( rows[i] );
351 emit categoriesChanged();
354 void QgsPointCloudClassifiedRendererModel::removeAllRows()
356 beginRemoveRows( QModelIndex(), 0, mCategories.size() - 1 );
359 emit categoriesChanged();
362 void QgsPointCloudClassifiedRendererModel::setCategoryColor(
int row,
const QColor &color )
364 mCategories[row].setColor( color );
365 emit dataChanged( createIndex( row, 0 ), createIndex( row, 0 ) );
366 emit categoriesChanged();
370 QgsPointCloudClassifiedRendererViewStyle::QgsPointCloudClassifiedRendererViewStyle( QWidget *parent )
374 void QgsPointCloudClassifiedRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
376 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
378 QStyleOption opt( *option );
379 opt.rect.setLeft( 0 );
381 opt.rect.setHeight( 0 );
383 opt.rect.setRight( widget->width() );
384 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
387 QProxyStyle::drawPrimitive( element, option, painter, widget );
396 mAttributeComboBox->setAllowEmptyAttributeName(
true );
399 mModel =
new QgsPointCloudClassifiedRendererModel(
this );
403 mAttributeComboBox->setLayer( layer );
405 setFromRenderer( layer->
renderer() );
408 viewCategories->setModel( mModel );
409 viewCategories->resizeColumnToContents( 0 );
410 viewCategories->resizeColumnToContents( 1 );
411 viewCategories->resizeColumnToContents( 2 );
413 viewCategories->setStyle(
new QgsPointCloudClassifiedRendererViewStyle( viewCategories ) );
416 this, &QgsPointCloudClassifiedRendererWidget::attributeChanged );
417 connect( mModel, &QgsPointCloudClassifiedRendererModel::categoriesChanged,
this, &QgsPointCloudClassifiedRendererWidget::emitWidgetChanged );
419 connect( viewCategories, &QAbstractItemView::doubleClicked,
this, &QgsPointCloudClassifiedRendererWidget::categoriesDoubleClicked );
420 connect( btnAddCategories, &QAbstractButton::clicked,
this, &QgsPointCloudClassifiedRendererWidget::addCategories );
421 connect( btnDeleteCategories, &QAbstractButton::clicked,
this, &QgsPointCloudClassifiedRendererWidget::deleteCategories );
422 connect( btnDeleteAllCategories, &QAbstractButton::clicked,
this, &QgsPointCloudClassifiedRendererWidget::deleteAllCategories );
423 connect( btnAddCategory, &QAbstractButton::clicked,
this, &QgsPointCloudClassifiedRendererWidget::addCategory );
429 return new QgsPointCloudClassifiedRendererWidget( layer, style );
439 std::unique_ptr< QgsPointCloudClassifiedRenderer > renderer = std::make_unique< QgsPointCloudClassifiedRenderer >();
440 renderer->setAttribute( mAttributeComboBox->currentAttribute() );
441 renderer->setCategories( mModel->categories() );
443 return renderer.release();
448 return mModel->categories();
451 QString QgsPointCloudClassifiedRendererWidget::attribute()
453 return mAttributeComboBox->currentAttribute();
456 void QgsPointCloudClassifiedRendererWidget::attributeChanged()
458 if ( mBlockChangedSignal )
461 mBlockChangedSignal =
true;
462 mModel->removeAllRows();
463 mBlockChangedSignal =
false;
467 void QgsPointCloudClassifiedRendererWidget::emitWidgetChanged()
469 if ( mBlockChangedSignal )
472 updateCategoriesPercentages();
473 emit widgetChanged();
476 void QgsPointCloudClassifiedRendererWidget::categoriesDoubleClicked(
const QModelIndex &idx )
478 if ( idx.isValid() && idx.column() == 0 )
479 changeCategorySymbol();
482 void QgsPointCloudClassifiedRendererWidget::addCategories()
484 if ( !mLayer || !mLayer->dataProvider() )
488 const QString currentAttribute = mAttributeComboBox->currentAttribute();
490 const QList<int> providerCategories = stats.
classesOf( currentAttribute );
494 const bool isClassificationAttribute = ! currentAttribute.compare( QStringLiteral(
"Classification" ), Qt::CaseInsensitive );
497 mBlockChangedSignal =
true;
498 for (
const int &providerCategory : providerCategories )
504 if (
c.value() == providerCategory )
515 if ( isClassificationAttribute )
519 if (
c.value() == providerCategory )
527 mModel->addCategory( category );
529 mBlockChangedSignal =
false;
533 void QgsPointCloudClassifiedRendererWidget::addCategory()
539 mModel->addCategory( cat );
542 void QgsPointCloudClassifiedRendererWidget::deleteCategories()
544 const QList<int> categoryIndexes = selectedCategories();
545 mModel->deleteRows( categoryIndexes );
548 void QgsPointCloudClassifiedRendererWidget::deleteAllCategories()
550 mModel->removeAllRows();
555 mBlockChangedSignal =
true;
558 mModel->setRendererCategories( classifiedRenderer->categories() );
559 mAttributeComboBox->setAttribute( classifiedRenderer->attribute() );
565 mBlockChangedSignal =
false;
569 void QgsPointCloudClassifiedRendererWidget::setFromCategories(
QgsPointCloudCategoryList categories,
const QString &attribute )
571 mBlockChangedSignal =
true;
572 mModel->setRendererCategories( categories );
573 if ( !attribute.isEmpty() )
575 mAttributeComboBox->setAttribute( attribute );
581 mBlockChangedSignal =
false;
585 void QgsPointCloudClassifiedRendererWidget::initialize()
587 if ( mAttributeComboBox->findText( QStringLiteral(
"Classification" ) ) > -1 )
589 mAttributeComboBox->setAttribute( QStringLiteral(
"Classification" ) );
593 mAttributeComboBox->setCurrentIndex( mAttributeComboBox->count() > 1 ? 1 : 0 );
595 mModel->removeAllRows();
599 void QgsPointCloudClassifiedRendererWidget::changeCategorySymbol()
601 const int row = currentCategoryRow();
617 mModel->setCategoryColor( row, newColor );
624 if ( newColor.isValid() )
626 mModel->setCategoryColor( row, newColor );
631 QList<int> QgsPointCloudClassifiedRendererWidget::selectedCategories()
634 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
635 for (
const QModelIndex &r : selectedRows )
639 rows.append( r.row() );
645 int QgsPointCloudClassifiedRendererWidget::currentCategoryRow()
647 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
648 if ( !idx.isValid() )
653 void QgsPointCloudClassifiedRendererWidget::updateCategoriesPercentages()
655 QMap < int, float > percentages;
666 if ( classes.contains( category.
value() ) || statsExact )
667 percentages.insert( category.
value(), (
double ) classes.value( category.
value() ) / pointCount * 100 );
669 mModel->updateCategoriesPercentages( percentages );