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