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