QGIS API Documentation 3.41.0-Master (3440c17df1d)
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" ).arg( QgsRasterAttributeTable::usageName( field.usage ),
98 QVariant::typeToName( field.type ),
99 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." );
109 }
110 return false;
111 }
112 return mRat->isValid( errorMessage );
113}
114
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
123 if ( ! editChecks( errorMessage ) )
124 {
125 return false;
126 }
127
128 if ( position < 0 )
129 {
130 if ( errorMessage )
131 {
132 *errorMessage = QObject::tr( "Invalid position '%1' for field insertion." ).arg( position );
133 }
134 return false;
135 }
136
137 const int newPosition { std::clamp( position, 0, static_cast<int>( mRat->fields().count( ) ) ) };
138 const QgsRasterAttributeTable::Field field { name, usage, type };
139 beginResetModel( );
140 const bool retVal { mRat->insertField( newPosition, field, errorMessage ) };
141 endResetModel();
142 return retVal;
143}
144
145bool QgsRasterAttributeTableModel::insertField( const int position, const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QVariant::Type type, QString *errorMessage )
146{
147 return insertField( position, name, usage, QgsVariantUtils::variantTypeToMetaType( type ), errorMessage );
148}
149
150
151bool QgsRasterAttributeTableModel::removeField( const int position, QString *errorMessage )
152{
153
154 if ( ! editChecks( errorMessage ) )
155 {
156 return false;
157 }
158
159 if ( position < 0 || position >= mRat->fields().count() )
160 {
161 if ( errorMessage )
162 {
163 *errorMessage = QObject::tr( "Invalid position '%1' for field removal." ).arg( position );
164 }
165 return false;
166 }
167
168 beginResetModel( );
169 const bool retVal { mRat->removeField( mRat->fields().at( position ).name, errorMessage ) };
170 endResetModel();
171 return retVal;
172}
173
175{
176
177 if ( ! editChecks( errorMessage ) )
178 {
179 return false;
180 }
181
182 if ( ! mRat->hasColor() && ! mRat->hasRamp( ) )
183 {
184 if ( errorMessage )
185 {
186 *errorMessage = tr( "Raster attribute table does not have color or ramp information." );
187 }
188 return false;
189 }
190
191 bool ret { true };
192 beginResetModel();
193 const QList<QgsRasterAttributeTable::Field> ratFields { mRat->fields() };
194 for ( const QgsRasterAttributeTable::Field &f : std::as_const( ratFields ) )
195 {
196 if ( f.isColor() || f.isRamp() )
197 {
198 ret &= mRat->removeField( f.name, errorMessage );
199 if ( ! ret )
200 {
201 break;
202 }
203 }
204 }
205 endResetModel();
206 return ret;
207}
208
209bool QgsRasterAttributeTableModel::insertRow( const int position, const QVariantList &rowData, QString *errorMessage )
210{
211 if ( ! editChecks( errorMessage ) )
212 {
213 return false;
214 }
215
216 if ( position < 0 || position > mRat->data().count( ) )
217 {
218 if ( errorMessage )
219 {
220 *errorMessage = tr( "Position is not valid or the table is empty." );
221 }
222 return false;
223 }
224
225 beginResetModel();
226 const bool retVal { mRat->insertRow( position, rowData, errorMessage ) };
227 endResetModel();
228 return retVal;
229}
230
231bool QgsRasterAttributeTableModel::insertColor( int position, QString *errorMessage )
232{
233 if ( ! editChecks( errorMessage ) )
234 {
235 return false;
236 }
237
238 if ( position < 0 )
239 {
240 if ( errorMessage )
241 {
242 *errorMessage = QObject::tr( "Invalid position '%1' for color insertion." ).arg( position );
243 }
244 return false;
245 }
246
247 beginResetModel();
248 const bool retVal { mRat->insertColor( position, errorMessage ) };
249 endResetModel();
250 return retVal;
251}
252
253bool QgsRasterAttributeTableModel::insertRamp( int position, QString *errorMessage )
254{
255 if ( ! editChecks( errorMessage ) )
256 {
257 return false;
258 }
259
260 if ( position < 0 )
261 {
262 if ( errorMessage )
263 {
264 *errorMessage = QObject::tr( "Invalid position '%1' for color ramp insertion." ).arg( position );
265 }
266 return false;
267 }
268
269 beginResetModel();
270 const bool retVal { mRat->insertRamp( position, errorMessage ) };
271 endResetModel();
272 return retVal;
273}
274
275bool QgsRasterAttributeTableModel::removeRow( const int position, QString *errorMessage )
276{
277 if ( ! editChecks( errorMessage ) )
278 {
279 return false;
280 }
281
282 if ( position < 0 || position >= mRat->data().count( ) )
283 {
284 if ( errorMessage )
285 {
286 *errorMessage = tr( "Position is not valid or the table is empty." );
287 }
288 return false;
289 }
290
291 beginResetModel();
292 const bool retVal { mRat->removeRow( position, errorMessage ) };
293 endResetModel();
294 return retVal;
295}
296
297bool QgsRasterAttributeTableModel::editChecks( QString *errorMessage )
298{
299 if ( ! mRat )
300 {
301 if ( errorMessage )
302 {
303 *errorMessage = QObject::tr( "Raster Attribute Table is not set for this model." );
304 }
305 return false;
306 }
307
308 if ( ! mEditable )
309 {
310 if ( errorMessage )
311 {
312 *errorMessage = QObject::tr( "Raster Attribute Table is not editable." );
313 }
314 return false;
315 }
316
317 return true;
318}
319
320QString QgsRasterAttributeTableModel::ratColorHeaderName() const
321{
322 return tr( "Color" );
323}
324
325int QgsRasterAttributeTableModel::rowCount( const QModelIndex &parent ) const
326{
327 return ( !parent.isValid() && mRat ) ? mRat->data().count() : 0;
328}
329
330int QgsRasterAttributeTableModel::columnCount( const QModelIndex &parent ) const
331{
332 return ( ! parent.isValid() && mRat ) ? ( mRat->fields().count() + ( mRat->hasColor() || mRat->hasRamp() ? 1 : 0 ) ) : 0;
333}
334
335QVariant QgsRasterAttributeTableModel::data( const QModelIndex &index, int role ) const
336{
337 if ( mRat && index.isValid() && index.row() < rowCount( QModelIndex() ) && index.column() < columnCount( QModelIndex() ) )
338 {
339 const QString fieldName { headerNames().at( index.column() ) };
340 const bool isColorOrRamp { ( hasColor() || hasRamp() ) && index.column() == columnCount( QModelIndex() ) - 1 }; // *NOPAD*
341 bool ok;
342 const QgsRasterAttributeTable::Field field { mRat->fieldByName( fieldName, &ok ) };
343 if ( ! isColorOrRamp && ! ok )
344 {
345 return QVariant();
346 }
347 if ( isColorOrRamp && hasColor() )
348 {
349 switch ( role )
350 {
351 case Qt::ItemDataRole::ForegroundRole:
352 {
353 // Choose black or white for a decent contrast.
354 const QColor tempColor { mRat->color( index.row() ) };
355 const double darkness { 1 - ( 0.299 * tempColor.red() + 0.587 * tempColor.green() + 0.114 * tempColor.blue() ) / 255};
356 return darkness > 0.5 ? QColor( Qt::GlobalColor::white ) : QColor( Qt::GlobalColor::black );
357 }
358 case Qt::ItemDataRole::EditRole:
359 case Qt::ItemDataRole::BackgroundRole:
360 return mRat->color( index.row() );
361 case Qt::ItemDataRole::DisplayRole:
362 return mRat->color( index.row() ).name();
363 default:
364 return QVariant();
365 }
366 }
367 else if ( isColorOrRamp && hasRamp() )
368 {
369 switch ( role )
370 {
371 case Qt::ItemDataRole::BackgroundRole:
372 {
373 return QVariant();
374 // This doesn't work (even if it should), so after a large amount
375 // of wasted hours I had to settle for ColorRampDelegate::paint override
376 /*
377 const QgsGradientColorRamp ramp { mRat->ramp( index.row() )};
378 QLinearGradient gradient( QPointF(0, 0), QPointF(1, 0) );
379 gradient.setCoordinateMode( QGradient::CoordinateMode::ObjectBoundingMode );
380 gradient.setColorAt(0, ramp.color1() );
381 gradient.setColorAt(1, ramp.color2() );
382 return QBrush{ gradient };
383 */
384 }
385 case Qt::ItemDataRole::EditRole:
386 {
387 return QVariant::fromValue( mRat->ramp( index.row() ) );
388 }
389 default:
390 return QVariant();
391 }
392 }
393 else if ( role == Qt::ItemDataRole::TextAlignmentRole && field.type != QMetaType::Type::QString )
394 {
395 return QVariant( Qt::AlignmentFlag::AlignRight | Qt::AlignmentFlag::AlignVCenter );
396 }
397 else if ( role == Qt::ItemDataRole::ToolTipRole && ( isColorOrRamp ) )
398 {
399 return tr( "This data is part of a color definition: click on '%1' column to edit." ).arg( ratColorHeaderName() );
400 }
401 else if ( role == Qt::ItemDataRole::DisplayRole || role == Qt::ItemDataRole::EditRole )
402 {
403 return mRat->data().at( index.row() ).at( index.column() );
404 }
405 else if ( role == Qt::ItemDataRole::FontRole && ( isColorOrRamp ) )
406 {
407 QFont font;
408 font.setItalic( true );
409 return font;
410 }
411 }
412 return QVariant();
413}
414
415bool QgsRasterAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
416{
417 if ( mRat && index.isValid() && role == Qt::ItemDataRole::EditRole )
418 {
419 const QString fieldName { headerNames().at( index.column() ) };
420 const bool isColorOrRamp { ( hasColor() || hasRamp() ) &&index.column() == columnCount( QModelIndex( ) ) - 1 };
421 bool ok;
422 const QgsRasterAttributeTable::Field field { mRat->fieldByName( fieldName, &ok ) };
423 if ( ! isColorOrRamp && ! ok )
424 {
425 return false;
426 }
427 if ( hasColor() && isColorOrRamp )
428 {
429 if ( ! value.canConvert( QMetaType::Type::QColor ) || ! mRat->setColor( index.row(), value.value<QColor>( ) ) )
430 {
431 return false;
432 }
433 const QModelIndex colorColIdx { QgsRasterAttributeTableModel::index( index.row(), columnCount( QModelIndex() ) - 1, QModelIndex() )};
434 emit dataChanged( colorColIdx, colorColIdx );
435 // Change all color columns
436 const QList<QgsRasterAttributeTable::Field> &ratFields { mRat->fields() };
437 for ( int fIdx = 0; fIdx < ratFields.count(); ++fIdx )
438 {
439 if ( ratFields[ fIdx ].isColor() )
440 {
441 const QModelIndex fieldColIdx { QgsRasterAttributeTableModel::index( index.row(), fIdx, QModelIndex() )};
442 emit dataChanged( fieldColIdx, fieldColIdx );
443 }
444 }
445 return true;
446 }
447 else if ( hasRamp() && isColorOrRamp )
448 {
449 const QgsGradientColorRamp ramp { qvariant_cast<QgsGradientColorRamp>( value ) };
450 if ( ! mRat->setRamp( index.row(), ramp.color1(), ramp.color2() ) )
451 {
452 return false;
453 }
454 const QModelIndex colorColIdx { QgsRasterAttributeTableModel::index( index.row(), columnCount( QModelIndex() ) - 1, QModelIndex() )};
455 emit dataChanged( colorColIdx, colorColIdx );
456 // Change all ramp columns
457 const QList<QgsRasterAttributeTable::Field> &ratFields { mRat->fields() };
458 for ( int fIdx = 0; fIdx < ratFields.count(); ++fIdx )
459 {
460 if ( ratFields[ fIdx ].isRamp() )
461 {
462 const QModelIndex fieldColIdx { QgsRasterAttributeTableModel::index( index.row(), fIdx, QModelIndex() )};
463 emit dataChanged( fieldColIdx, fieldColIdx );
464 }
465 }
466 return true;
467 }
468 else if ( ok )
469 {
470 const bool retVal { mRat->setValue( index.row(), index.column(), value ) };
471 if ( retVal )
472 {
473 const QModelIndex fieldColIdx { QgsRasterAttributeTableModel::index( index.row(), index.column(), QModelIndex() )};
474 emit dataChanged( fieldColIdx, fieldColIdx );
475 }
476 return retVal;
477 }
478 }
479 return false;
480}
481
482QVariant QgsRasterAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
483{
484 if ( orientation == Qt::Orientation::Horizontal )
485 {
486 const QStringList hNames { headerNames( ) };
487 if ( section < hNames.length() )
488 {
489 switch ( role )
490 {
491 case Qt::ItemDataRole::DisplayRole:
492 {
493 return hNames.at( section );
494 }
495 case Qt::ItemDataRole::ToolTipRole:
496 {
497 return headerTooltip( section );
498 }
499 default:
500 return QAbstractTableModel::headerData( section, orientation, role );
501 }
502 }
503 }
504 return QAbstractTableModel::headerData( section, orientation, role );
505}
506
507Qt::ItemFlags QgsRasterAttributeTableModel::flags( const QModelIndex &index ) const
508{
509 if ( mRat )
510 {
511 Qt::ItemFlags flags;
512 if ( index.isValid() )
513 {
514 flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
515 if ( mEditable )
516 {
517 if ( index.column() < mRat->fields().count( ) )
518 {
519 const QList<QgsRasterAttributeTable::Field> fields = mRat->fields();
520 const QgsRasterAttributeTable::Field &field { fields.at( index.column() ) };
521 if ( ! field.isColor() && ! field.isRamp() )
522 {
523 flags |= Qt::ItemIsEditable;
524 }
525 }
526 else // Must be the color column
527 {
528 flags |= Qt::ItemIsEditable;
529 }
530 }
531 }
532 return flags;
533 }
534 return Qt::NoItemFlags;
535}
RasterAttributeTableFieldUsage
The RasterAttributeTableFieldUsage enum represents the usage of a Raster Attribute Table field.
Definition qgis.h:1434
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.