QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsrasterattributetablemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterattributetablemodel.cpp - QgsRasterAttributeTableModel
3
4 ---------------------
5 begin : 29.9.2022
6 copyright : (C) 2022 by ale
7 email : [your-email-here]
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
17
18#include <QColor>
19#include <QFont>
20
21#include "moc_qgsrasterattributetablemodel.cpp"
22
24 : QAbstractTableModel( parent )
25 , mRat( rat )
26{
27}
28
30{
31 return mEditable;
32}
33
35{
36 mEditable = editable;
37}
38
40{
41 return mRat && mRat->hasColor();
42}
43
45{
46 return mRat && mRat->hasRamp();
47}
48
50{
51 QStringList headers;
52 if ( mRat )
53 {
54 const QList<QgsRasterAttributeTable::Field> &ratFields { mRat->fields() };
55 for ( const QgsRasterAttributeTable::Field &f : std::as_const( ratFields ) )
56 {
57 headers.push_back( f.name );
58 }
59
60 if ( hasColor() || hasRamp() )
61 {
62 headers.append( ratColorHeaderName() );
63 }
64 }
65 return headers;
66}
67
68QString QgsRasterAttributeTableModel::headerTooltip( const int section ) const
69{
70 const QStringList hNames { headerNames() };
71 if ( section < 0 || section >= hNames.count() )
72 {
73 return QString();
74 }
75
76 const QString fieldName { hNames.at( section ) };
77 const bool isColor { hasColor() && section == hNames.count() - 1 }; // *NOPAD*
78
79 if ( isColor )
80 {
81 return tr( "Virtual color field generated from the values in RGB(A) data columns" );
82 }
83
84 bool ok;
85 const QgsRasterAttributeTable::Field field { mRat->fieldByName( fieldName, &ok ) };
86
87 if ( !ok )
88 {
89 return QString();
90 }
91
92 return QStringLiteral( R"HTML(
93 <dl>
94 <dt>Role</dt><dd>%1</dd>
95 <dt>Type</dt><dd>%2</dd>
96 <dt>Description</dt><dd>%3</dd>
97 </dl>
98 )HTML" )
99 .arg( QgsRasterAttributeTable::usageName( field.usage ), QVariant::typeToName( field.type ), QgsRasterAttributeTable::usageInformation().value( field.usage ).description );
100}
101
102bool QgsRasterAttributeTableModel::isValid( QString *errorMessage )
103{
104 if ( !mRat )
105 {
106 if ( errorMessage )
107 {
108 *errorMessage = tr( "Raster Attribute Table is not set for this model." );
110 return false;
111 }
112 return mRat->isValid( errorMessage );
113}
116{
117 return mRat && mRat->isDirty();
118}
119
120bool QgsRasterAttributeTableModel::insertField( const int position, const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QMetaType::Type type, QString *errorMessage )
121{
122 if ( !editChecks( errorMessage ) )
123 {
124 return false;
125 }
126
127 if ( position < 0 )
128 {
129 if ( errorMessage )
130 {
131 *errorMessage = QObject::tr( "Invalid position '%1' for field insertion." ).arg( position );
132 }
133 return false;
134 }
135
136 const int newPosition { std::clamp( position, 0, static_cast<int>( mRat->fields().count() ) ) };
137 const QgsRasterAttributeTable::Field field { name, usage, type };
138 beginResetModel();
139 const bool retVal { mRat->insertField( newPosition, field, errorMessage ) };
140 endResetModel();
141 return retVal;
142}
143
144bool QgsRasterAttributeTableModel::insertField( const int position, const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QVariant::Type type, QString *errorMessage )
145{
146 return insertField( position, name, usage, QgsVariantUtils::variantTypeToMetaType( type ), errorMessage );
147}
148
149
150bool QgsRasterAttributeTableModel::removeField( const int position, QString *errorMessage )
151{
152 if ( !editChecks( errorMessage ) )
153 {
154 return false;
155 }
156
157 if ( position < 0 || position >= mRat->fields().count() )
158 {
159 if ( errorMessage )
160 {
161 *errorMessage = QObject::tr( "Invalid position '%1' for field removal." ).arg( position );
162 }
163 return false;
164 }
165
166 beginResetModel();
167 const bool retVal { mRat->removeField( mRat->fields().at( position ).name, errorMessage ) };
168 endResetModel();
169 return retVal;
170}
171
172bool QgsRasterAttributeTableModel::removeColorOrRamp( QString *errorMessage )
173{
174 if ( !editChecks( errorMessage ) )
175 {
176 return false;
177 }
178
179 if ( !mRat->hasColor() && !mRat->hasRamp() )
180 {
181 if ( errorMessage )
182 {
183 *errorMessage = tr( "Raster attribute table does not have color or ramp information." );
184 }
185 return false;
186 }
187
188 bool ret { true };
189 beginResetModel();
190 const QList<QgsRasterAttributeTable::Field> ratFields { mRat->fields() };
191 for ( const QgsRasterAttributeTable::Field &f : std::as_const( ratFields ) )
192 {
193 if ( f.isColor() || f.isRamp() )
194 {
195 ret &= mRat->removeField( f.name, errorMessage );
196 if ( !ret )
197 {
198 break;
199 }
201 }
202 endResetModel();
203 return ret;
204}
205
206bool QgsRasterAttributeTableModel::insertRow( const int position, const QVariantList &rowData, QString *errorMessage )
207{
208 if ( !editChecks( errorMessage ) )
209 {
210 return false;
211 }
212
213 if ( position < 0 || position > mRat->data().count() )
214 {
215 if ( errorMessage )
216 {
217 *errorMessage = tr( "Position is not valid or the table is empty." );
218 }
219 return false;
220 }
221
222 beginResetModel();
223 const bool retVal { mRat->insertRow( position, rowData, errorMessage ) };
224 endResetModel();
225 return retVal;
226}
227
228bool QgsRasterAttributeTableModel::insertColor( int position, QString *errorMessage )
229{
230 if ( !editChecks( errorMessage ) )
231 {
232 return false;
233 }
234
235 if ( position < 0 )
236 {
237 if ( errorMessage )
238 {
239 *errorMessage = QObject::tr( "Invalid position '%1' for color insertion." ).arg( position );
240 }
241 return false;
242 }
243
244 beginResetModel();
245 const bool retVal { mRat->insertColor( position, errorMessage ) };
246 endResetModel();
247 return retVal;
248}
249
250bool QgsRasterAttributeTableModel::insertRamp( int position, QString *errorMessage )
251{
252 if ( !editChecks( errorMessage ) )
253 {
254 return false;
255 }
256
257 if ( position < 0 )
258 {
259 if ( errorMessage )
260 {
261 *errorMessage = QObject::tr( "Invalid position '%1' for color ramp insertion." ).arg( position );
262 }
263 return false;
264 }
265
266 beginResetModel();
267 const bool retVal { mRat->insertRamp( position, errorMessage ) };
268 endResetModel();
269 return retVal;
270}
271
272bool QgsRasterAttributeTableModel::removeRow( const int position, QString *errorMessage )
273{
274 if ( !editChecks( errorMessage ) )
275 {
276 return false;
277 }
278
279 if ( position < 0 || position >= mRat->data().count() )
280 {
281 if ( errorMessage )
282 {
283 *errorMessage = tr( "Position is not valid or the table is empty." );
284 }
285 return false;
286 }
287
288 beginResetModel();
289 const bool retVal { mRat->removeRow( position, errorMessage ) };
290 endResetModel();
291 return retVal;
292}
293
294bool QgsRasterAttributeTableModel::editChecks( QString *errorMessage )
295{
296 if ( !mRat )
297 {
298 if ( errorMessage )
299 {
300 *errorMessage = QObject::tr( "Raster Attribute Table is not set for this model." );
301 }
302 return false;
303 }
304
305 if ( !mEditable )
306 {
307 if ( errorMessage )
308 {
309 *errorMessage = QObject::tr( "Raster Attribute Table is not editable." );
310 }
311 return false;
312 }
313
314 return true;
315}
317QString QgsRasterAttributeTableModel::ratColorHeaderName() const
318{
319 return tr( "Color" );
320}
322int QgsRasterAttributeTableModel::rowCount( const QModelIndex &parent ) const
323{
324 return ( !parent.isValid() && mRat ) ? mRat->data().count() : 0;
325}
327int QgsRasterAttributeTableModel::columnCount( const QModelIndex &parent ) const
328{
329 return ( !parent.isValid() && mRat ) ? ( mRat->fields().count() + ( mRat->hasColor() || mRat->hasRamp() ? 1 : 0 ) ) : 0;
330}
331
332QVariant QgsRasterAttributeTableModel::data( const QModelIndex &index, int role ) const
333{
334 if ( mRat && index.isValid() && index.row() < rowCount( QModelIndex() ) && index.column() < columnCount( QModelIndex() ) )
335 {
336 const QString fieldName { headerNames().at( index.column() ) };
337 const bool isColorOrRamp { ( hasColor() || hasRamp() ) && index.column() == columnCount( QModelIndex() ) - 1 }; // *NOPAD*
338 bool ok;
339 const QgsRasterAttributeTable::Field field { mRat->fieldByName( fieldName, &ok ) };
340 if ( !isColorOrRamp && !ok )
341 {
342 return QVariant();
343 }
344 if ( isColorOrRamp && hasColor() )
345 {
346 switch ( role )
347 {
348 case Qt::ItemDataRole::ForegroundRole:
349 {
350 // Choose black or white for a decent contrast.
351 const QColor tempColor { mRat->color( index.row() ) };
352 const double darkness { 1 - ( 0.299 * tempColor.red() + 0.587 * tempColor.green() + 0.114 * tempColor.blue() ) / 255 };
353 return darkness > 0.5 ? QColor( Qt::GlobalColor::white ) : QColor( Qt::GlobalColor::black );
354 }
355 case Qt::ItemDataRole::EditRole:
356 case Qt::ItemDataRole::BackgroundRole:
357 return mRat->color( index.row() );
358 case Qt::ItemDataRole::DisplayRole:
359 return mRat->color( index.row() ).name();
360 default:
361 return QVariant();
362 }
363 }
364 else if ( isColorOrRamp && hasRamp() )
365 {
366 switch ( role )
367 {
368 case Qt::ItemDataRole::BackgroundRole:
369 {
370 return QVariant();
371 // This doesn't work (even if it should), so after a large amount
372 // of wasted hours I had to settle for ColorRampDelegate::paint override
373 /*
374 const QgsGradientColorRamp ramp { mRat->ramp( index.row() )};
375 QLinearGradient gradient( QPointF(0, 0), QPointF(1, 0) );
376 gradient.setCoordinateMode( QGradient::CoordinateMode::ObjectBoundingMode );
377 gradient.setColorAt(0, ramp.color1() );
378 gradient.setColorAt(1, ramp.color2() );
379 return QBrush{ gradient };
380 */
381 }
382 case Qt::ItemDataRole::EditRole:
383 {
384 return QVariant::fromValue( mRat->ramp( index.row() ) );
385 }
386 default:
387 return QVariant();
388 }
389 }
390 else if ( role == Qt::ItemDataRole::TextAlignmentRole && field.type != QMetaType::Type::QString )
391 {
392 return QVariant( Qt::AlignmentFlag::AlignRight | Qt::AlignmentFlag::AlignVCenter );
393 }
394 else if ( role == Qt::ItemDataRole::ToolTipRole && ( isColorOrRamp ) )
395 {
396 return tr( "This data is part of a color definition: click on '%1' column to edit." ).arg( ratColorHeaderName() );
397 }
398 else if ( role == Qt::ItemDataRole::DisplayRole || role == Qt::ItemDataRole::EditRole )
399 {
400 return mRat->data().at( index.row() ).at( index.column() );
401 }
402 else if ( role == Qt::ItemDataRole::FontRole && ( isColorOrRamp ) )
403 {
404 QFont font;
405 font.setItalic( true );
406 return font;
407 }
408 }
409 return QVariant();
410}
411
412bool QgsRasterAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
413{
414 if ( mRat && index.isValid() && role == Qt::ItemDataRole::EditRole )
415 {
416 const QString fieldName { headerNames().at( index.column() ) };
417 const bool isColorOrRamp { ( hasColor() || hasRamp() ) && index.column() == columnCount( QModelIndex() ) - 1 };
418 bool ok;
419 const QgsRasterAttributeTable::Field field { mRat->fieldByName( fieldName, &ok ) };
420 if ( !isColorOrRamp && !ok )
421 {
422 return false;
423 }
424 if ( hasColor() && isColorOrRamp )
425 {
426 if ( !value.canConvert( QMetaType::Type::QColor ) || !mRat->setColor( index.row(), value.value<QColor>() ) )
427 {
428 return false;
429 }
430 const QModelIndex colorColIdx { QgsRasterAttributeTableModel::index( index.row(), columnCount( QModelIndex() ) - 1, QModelIndex() ) };
431 emit dataChanged( colorColIdx, colorColIdx );
432 // Change all color columns
433 const QList<QgsRasterAttributeTable::Field> &ratFields { mRat->fields() };
434 for ( int fIdx = 0; fIdx < ratFields.count(); ++fIdx )
435 {
436 if ( ratFields[fIdx].isColor() )
437 {
438 const QModelIndex fieldColIdx { QgsRasterAttributeTableModel::index( index.row(), fIdx, QModelIndex() ) };
439 emit dataChanged( fieldColIdx, fieldColIdx );
440 }
441 }
442 return true;
443 }
444 else if ( hasRamp() && isColorOrRamp )
445 {
446 const QgsGradientColorRamp ramp { qvariant_cast<QgsGradientColorRamp>( value ) };
447 if ( !mRat->setRamp( index.row(), ramp.color1(), ramp.color2() ) )
448 {
449 return false;
450 }
451 const QModelIndex colorColIdx { QgsRasterAttributeTableModel::index( index.row(), columnCount( QModelIndex() ) - 1, QModelIndex() ) };
452 emit dataChanged( colorColIdx, colorColIdx );
453 // Change all ramp columns
454 const QList<QgsRasterAttributeTable::Field> &ratFields { mRat->fields() };
455 for ( int fIdx = 0; fIdx < ratFields.count(); ++fIdx )
456 {
457 if ( ratFields[fIdx].isRamp() )
458 {
459 const QModelIndex fieldColIdx { QgsRasterAttributeTableModel::index( index.row(), fIdx, QModelIndex() ) };
460 emit dataChanged( fieldColIdx, fieldColIdx );
461 }
462 }
463 return true;
464 }
465 else if ( ok )
466 {
467 const bool retVal { mRat->setValue( index.row(), index.column(), value ) };
468 if ( retVal )
469 {
470 const QModelIndex fieldColIdx { QgsRasterAttributeTableModel::index( index.row(), index.column(), QModelIndex() ) };
471 emit dataChanged( fieldColIdx, fieldColIdx );
472 }
473 return retVal;
474 }
475 }
476 return false;
477}
478
479QVariant QgsRasterAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
480{
481 if ( orientation == Qt::Orientation::Horizontal )
482 {
483 const QStringList hNames { headerNames() };
484 if ( section < hNames.length() )
485 {
486 switch ( role )
487 {
488 case Qt::ItemDataRole::DisplayRole:
489 {
490 return hNames.at( section );
491 }
492 case Qt::ItemDataRole::ToolTipRole:
493 {
494 return headerTooltip( section );
495 }
496 default:
497 return QAbstractTableModel::headerData( section, orientation, role );
499 }
500 }
501 return QAbstractTableModel::headerData( section, orientation, role );
502}
503
504Qt::ItemFlags QgsRasterAttributeTableModel::flags( const QModelIndex &index ) const
505{
506 if ( mRat )
507 {
508 Qt::ItemFlags flags;
509 if ( index.isValid() )
510 {
511 flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
512 if ( mEditable )
513 {
514 if ( index.column() < mRat->fields().count() )
515 {
516 const QList<QgsRasterAttributeTable::Field> fields = mRat->fields();
517 const QgsRasterAttributeTable::Field &field { fields.at( index.column() ) };
518 if ( !field.isColor() && !field.isRamp() )
519 {
520 flags |= Qt::ItemIsEditable;
521 }
522 }
523 else // Must be the color column
524 {
525 flags |= Qt::ItemIsEditable;
526 }
527 }
528 }
529 return flags;
530 }
531 return Qt::NoItemFlags;
532}
RasterAttributeTableFieldUsage
The RasterAttributeTableFieldUsage enum represents the usage of a Raster Attribute Table field.
Definition qgis.h:1587
QColor color1() const
Returns the gradient start color.
QColor color2() const
Returns the gradient end color.
QStringList headerNames() const
Returns all the header names, including the "virtual" color header if the Raster Attribute Table has ...
bool removeColorOrRamp(QString *errorMessage=nullptr)
Removes all color or ramp information, optionally reporting any error in errorMessage,...
bool hasRamp() const
Returns true if the Raster Attribute Table has ramp information.
int rowCount(const QModelIndex &parent) const override
bool isDirty()
Returns true if the Raster Attribute Table was modified since it was last saved or read.
bool hasColor() const
Returns true if the Raster Attribute Table has color information.
bool removeField(const int position, QString *errorMessage=nullptr)
Remove the field at given position, optionally reporting any error in errorMessage,...
bool insertColor(int position, QString *errorMessage=nullptr)
Create RGBA fields and inserts them at position, optionally reporting any error in errorMessage,...
bool insertRow(const int position, const QVariantList &rowData, QString *errorMessage=nullptr)
Inserts a new row before position, optionally reporting any error in errorMessage,...
int columnCount(const QModelIndex &parent) const override
bool editable() const
Returns true if the Raster Attribute Table is editable.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
QString headerTooltip(const int section) const
Returns the tooltip for the given section.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
void setEditable(bool editable)
Sets the Raster Attribute Table editable state to editable.
QgsRasterAttributeTableModel(QgsRasterAttributeTable *rat, QObject *parent=nullptr)
Creates a new QgsRasterAttributeTableModel from raster attribute table rat and optional parent.
bool insertField(const int position, const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QMetaType::Type type, QString *errorMessage=nullptr)
Inserts a field at the given position.
bool isValid(QString *errorMessage=nullptr)
Checks if the Raster Attribute Table is valid, optionally returns validation errors in errorMessage.
QVariant data(const QModelIndex &index, int role) const override
bool insertRamp(int position, QString *errorMessage=nullptr)
Create RGBA minimum and maximum fields and inserts them at position, optionally reporting any error i...
Qt::ItemFlags flags(const QModelIndex &index) const override
bool removeRow(const int position, QString *errorMessage=nullptr)
Removes the row at position, optionally reporting any error in errorMessage, returns true on success.
The Field class represents a Raster Attribute Table field, including its name, usage and type.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
Represents a Raster Attribute Table (RAT).
static QHash< Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation > usageInformation()
Returns information about supported Raster Attribute Table usages.
static QString usageName(const Qgis::RasterAttributeTableFieldUsage fieldusage)
Returns the translated human readable name of fieldUsage.
static QMetaType::Type variantTypeToMetaType(QVariant::Type variantType)
Converts a QVariant::Type to a QMetaType::Type.