QGIS API Documentation 4.1.0-Master (0cdd3ae6384)
Loading...
Searching...
No Matches
qgsrasterattributetable.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterattributetable.cpp - QgsRasterAttributeTable
3
4 ---------------------
5 begin : 3.12.2021
6 copyright : (C) 2021 by Alessandro Pasotti
7 email : elpaso at itopen dot it
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 ***************************************************************************/
16
18
19#include <cmath>
20#include <memory>
21#include <mutex>
22
23#include "qgsfileutils.h"
24#include "qgsogrprovider.h"
26#include "qgsrasterlayer.h"
27#include "qgsrastershader.h"
30#include "qgsvectorfilewriter.h"
31
32#include <QLocale>
33#include <QString>
34
35using namespace Qt::StringLiterals;
36
38std::once_flag usageInformationLoaderFlag;
39QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> QgsRasterAttributeTable::sUsageInformation;
41
42
47
48
50{
51 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
52 return fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Red )
53 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Green )
54 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Blue );
55}
56
57bool QgsRasterAttributeTable::setColor( const int row, const QColor &color )
58{
59 if ( !hasColor() || row < 0 || row >= mData.count() )
60 {
61 return false;
62 }
63
64 for ( int idx = 0; idx < mFields.count(); ++idx )
65 {
66 const Field f { mFields.at( idx ) };
67 switch ( f.usage )
68 {
70 setValue( row, idx, color.red() );
71 break;
73 setValue( row, idx, color.green() );
74 break;
76 setValue( row, idx, color.blue() );
77 break;
79 setValue( row, idx, color.alpha() );
80 break;
81 default:
82 break;
83 }
84 }
85 return true;
86}
87
88
89bool QgsRasterAttributeTable::setRamp( const int row, const QColor &colorMin, const QColor &colorMax )
90{
91 if ( !hasRamp() || row < 0 || row >= mData.count() )
92 {
93 return false;
94 }
95
96 int idx = 0;
97 for ( Field &f : mFields )
98 {
99 switch ( f.usage )
100 {
102 setValue( row, idx, colorMin.red() );
103 break;
105 setValue( row, idx, colorMin.green() );
106 break;
108 setValue( row, idx, colorMin.blue() );
109 break;
111 setValue( row, idx, colorMin.alpha() );
112 break;
114 setValue( row, idx, colorMax.red() );
115 break;
117 setValue( row, idx, colorMax.green() );
118 break;
120 setValue( row, idx, colorMax.blue() );
121 break;
123 setValue( row, idx, colorMax.alpha() );
124 break;
125 default:
126 break;
127 }
128 idx++;
129 }
130 return true;
131}
132
134{
135 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
136 return fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMin )
137 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMin )
138 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMin )
139 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMax )
140 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMax )
141 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMax );
142}
143
144
145QList<Qgis::RasterAttributeTableFieldUsage> QgsRasterAttributeTable::usages() const
146{
147 QList<Qgis::RasterAttributeTableFieldUsage> usages;
148 for ( const QgsRasterAttributeTable::Field &field : std::as_const( mFields ) )
149 {
150 usages.push_back( field.usage );
151 }
152 return usages;
153}
154
156QList<int> QgsRasterAttributeTable::intUsages() const
157{
158 QList<int> usages;
159 for ( const QgsRasterAttributeTable::Field &field : std::as_const( mFields ) )
160 {
161 usages.push_back( static_cast<int>( field.usage ) );
162 }
163 return usages;
164}
166
168{
169 QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
170 // No ramps support here
171 if ( hasColor()
172 && row < mData.count()
173 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Red )
174 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Green )
175 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Blue ) )
176 {
177 const QVariantList rowData = mData.at( row );
178 QColor color {
179 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Red ) ).toInt(),
180 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Green ) ).toInt(),
181 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Blue ) ).toInt()
182 };
183 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Alpha ) )
184 {
185 color.setAlpha( rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Alpha ) ).toInt() );
186 }
187 return color;
188 }
189 return QColor();
190}
191
193{
194 if ( !hasRamp() || row < 0 || row >= mData.count() )
195 {
196 return QgsGradientColorRamp();
197 }
198 QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
199 const QVariantList rowData = mData.at( row );
200 QColor colorMin {
201 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::RedMin ) ).toInt(),
202 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::GreenMin ) ).toInt(),
203 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::BlueMin ) ).toInt()
204 };
205 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::AlphaMin ) )
206 {
207 colorMin.setAlpha( rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::AlphaMin ) ).toInt() );
208 }
209 QColor colorMax {
210 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::RedMax ) ).toInt(),
211 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::GreenMax ) ).toInt(),
212 rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::BlueMax ) ).toInt()
213 };
214 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::AlphaMax ) )
215 {
216 colorMax.setAlpha( rowData.at( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::AlphaMax ) ).toInt() );
217 }
218 return QgsGradientColorRamp( colorMin, colorMax );
219}
220
221QList<QgsRasterAttributeTable::Field> QgsRasterAttributeTable::fields() const
222{
223 return mFields;
224}
225
227{
228 QgsFields qFields;
229
230 for ( const QgsRasterAttributeTable::Field &field : std::as_const( mFields ) )
231 {
232 qFields.append( QgsField( field.name, field.type ) );
233 }
234 return qFields;
235}
236
238{
239 QgsFeatureList features;
240 for ( const QVariantList &row : std::as_const( mData ) )
241 {
242 QgsAttributes attributes;
243 for ( const auto &cell : std::as_const( row ) )
244 {
245 attributes.append( cell );
246 }
247 QgsFeature feature { qgisFields() };
248 feature.setAttributes( attributes );
249 features.append( feature );
250 }
251 return features;
252}
253
255{
256 return mIsDirty;
257}
258
260{
261 mIsDirty = isDirty;
262}
263
264bool QgsRasterAttributeTable::insertField( int position, const Field &field, QString *errorMessage )
265{
266 const int realPos { std::clamp( position, 0, static_cast<int>( mFields.count() ) ) };
267
268 if ( field.name.isEmpty() )
269 {
270 if ( errorMessage )
271 {
272 *errorMessage = tr( "Field name must not be empty." );
273 }
274 return false;
275 }
276
277 // Check for duplicate names
278 bool ok;
279 fieldByName( field.name, &ok );
280
281 if ( ok )
282 {
283 if ( errorMessage )
284 {
285 *errorMessage = tr( "A field with name '%1' already exists." ).arg( field.name );
286 }
287 return false;
288 }
289
290 // Check for duplicate unique usages
291 static const QList<Qgis::RasterAttributeTableFieldUsage> uniqueUsages {
309 };
310
311 if ( uniqueUsages.contains( field.usage ) && !fieldsByUsage( field.usage ).isEmpty() )
312 {
313 if ( errorMessage )
314 {
315 *errorMessage = tr( "A field with unique usage '%1' already exists." ).arg( usageName( field.usage ) );
316 }
317 return false;
318 }
319
320 mFields.insert( realPos, field );
321
322 for ( auto it = mData.begin(); it != mData.end(); ++it )
323 {
324 QVariant defaultValue = QgsVariantUtils::createNullVariant( field.type );
325 // Set default values
326 switch ( field.type )
327 {
328 case QMetaType::Type::QChar:
329 case QMetaType::Type::Int:
330 case QMetaType::Type::UInt:
331 case QMetaType::Type::LongLong:
332 case QMetaType::Type::ULongLong:
333 case QMetaType::Type::Double:
334 defaultValue = 0;
335 break;
336 default:
337 defaultValue = QString();
338 }
339 it->insert( realPos, defaultValue );
340 }
341
342 // Set/change the table type from the value field type
344 {
346 }
348 {
350 }
351
352 setType();
353 setDirty( true );
354
355 return true;
356}
357
358bool QgsRasterAttributeTable::insertField( int position, const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QMetaType::Type type, QString *errorMessage )
359{
360 return insertField( position, { name, usage, type }, errorMessage );
361}
362
363bool QgsRasterAttributeTable::insertField( int position, const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QVariant::Type type, QString *errorMessage )
364{
365 return insertField( position, name, usage, QgsVariantUtils::variantTypeToMetaType( type ), errorMessage );
366}
367
368bool QgsRasterAttributeTable::insertColor( int position, QString *errorMessage )
369{
370 const QList<Qgis::RasterAttributeTableFieldUsage> colors {
372 };
373 int idx { position };
374 for ( const Qgis::RasterAttributeTableFieldUsage usage : std::as_const( colors ) )
375 {
376 if ( !insertField( idx, usageName( usage ), usage, QMetaType::Type::Int, errorMessage ) )
377 {
378 return false;
379 }
380 ++idx;
381 }
382 return true;
383}
384
386{
387 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
388 {
389 return false;
390 }
391
392 const Field field { fields().at( fieldIndex ) };
393 if ( !usageInformation()[usage].allowedTypes.contains( field.type ) )
394 {
395 return false;
396 }
397
398 mFields[fieldIndex].usage = usage;
399 setType();
400
401 return true;
402}
403
404bool QgsRasterAttributeTable::insertRamp( int position, QString *errorMessage )
405{
407 {
408 if ( errorMessage )
409 {
410 *errorMessage = tr( "A color ramp can only be added to an athematic attribute table." );
411 }
412 }
413 const QList<Qgis::RasterAttributeTableFieldUsage> colors {
422 };
423 int idx { position };
424 for ( const Qgis::RasterAttributeTableFieldUsage usage : std::as_const( colors ) )
425 {
426 if ( !insertField( idx, usageName( usage ), usage, QMetaType::Type::Int, errorMessage ) )
427 {
428 return false;
429 }
430 ++idx;
431 }
432 return true;
433}
434
435bool QgsRasterAttributeTable::appendField( const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QMetaType::Type type, QString *errorMessage )
436{
437 return insertField( static_cast<int>( mFields.count() ), name, usage, type, errorMessage );
438}
439
440bool QgsRasterAttributeTable::appendField( const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QVariant::Type type, QString *errorMessage )
441{
442 return appendField( name, usage, QgsVariantUtils::variantTypeToMetaType( type ), errorMessage );
443}
444
445bool QgsRasterAttributeTable::appendField( const Field &field, QString *errorMessage )
446{
447 return insertField( static_cast<int>( mFields.count() ), field, errorMessage );
448}
449
450bool QgsRasterAttributeTable::removeField( const QString &name, QString *errorMessage )
451{
452 const auto toRemove { std::find_if( mFields.begin(), mFields.end(), [&name]( Field &f ) -> bool { return f.name == name; } ) };
453
454 if ( toRemove != mFields.end() )
455 {
456 const int idx { static_cast<int>( std::distance( mFields.begin(), toRemove ) ) };
457 mFields.erase( toRemove );
458 for ( auto it = mData.begin(); it != mData.end(); ++it )
459 {
460 it->removeAt( idx );
461 }
462 setType();
463 setDirty( true );
464 return true;
465 }
466
467 if ( errorMessage )
468 {
469 *errorMessage = tr( "A field with name '%1' was not found." ).arg( name );
470 }
471 return false;
472}
473
474bool QgsRasterAttributeTable::insertRow( int position, const QVariantList &rowData, QString *errorMessage )
475{
476 const int realPos { std::clamp( position, 0, static_cast<int>( mData.count() ) ) };
477
478 if ( rowData.size() != mFields.size() )
479 {
480 if ( errorMessage )
481 {
482 *errorMessage = tr( "Row element count differs from field count (%1)." ).arg( mFields.size() );
483 }
484 return false;
485 }
486
487 QVariantList dataValid;
488
489 for ( int idx = 0; idx < mFields.count(); ++idx )
490 {
491 const QMetaType::Type type = mFields.at( idx ).type;
492 QVariant cell( rowData[idx] );
493 if ( type == QMetaType::Type::QDateTime && cell.toString().isEmpty() )
494 {
495 dataValid.append( QVariant() );
496 }
497 else if ( !cell.canConvert( type ) || !cell.convert( type ) )
498 {
499 if ( errorMessage )
500 {
501 *errorMessage = tr( "Row data at column %1 cannot be converted to field type (%2)." ).arg( idx ).arg( QVariant::typeToName( type ) );
502 }
503 return false;
504 }
505 else
506 {
507 dataValid.append( cell );
508 }
509 }
510
511 mData.insert( realPos, dataValid );
512 setDirty( true );
513 return true;
514}
515
516bool QgsRasterAttributeTable::removeRow( int position, QString *errorMessage )
517{
518 if ( position >= mData.count() || position < 0 || mData.isEmpty() )
519 {
520 if ( errorMessage )
521 {
522 *errorMessage = tr( "Position is not valid or the table is empty." );
523 }
524 return false;
525 }
526 mData.removeAt( position );
527 setDirty( true );
528 return true;
529}
530
531bool QgsRasterAttributeTable::appendRow( const QVariantList &data, QString *errorMessage )
532{
533 return insertRow( static_cast<int>( mData.count() ), data, errorMessage );
534}
535
536bool QgsRasterAttributeTable::writeToFile( const QString &path, QString *errorMessage )
537{
540 options.driverName = u"ESRI Shapefile"_s;
541 options.fileEncoding = u"UTF-8"_s;
542 options.layerOptions = QStringList() << u"SHPT=NULL"_s;
543
544 std::unique_ptr<QgsVectorFileWriter> writer;
545
546 // Strip .dbf from path because OGR adds it back
547 QString cleanedPath { path };
548 if ( path.endsWith( u".dbf"_s, Qt::CaseSensitivity::CaseInsensitive ) )
549 {
550 cleanedPath.chop( 4 );
551 }
552
553 cleanedPath = QgsFileUtils::ensureFileNameHasExtension( cleanedPath, { { u".vat"_s } } );
554
556
557 cleanedPath.append( u".dbf"_s );
558
559 const QgsVectorFileWriter::WriterError error { writer->hasError() };
561 {
562 if ( errorMessage )
563 {
564 *errorMessage = tr( "Error creating Raster Attribute Table table: %1." ).arg( writer->errorMessage() );
565 }
566 return false;
567 }
568
569 QgsFeatureList features { qgisFeatures() };
570 bool result { writer->addFeatures( features ) };
571
572 if ( !result )
573 {
574 if ( errorMessage )
575 {
576 *errorMessage = tr( "Error creating Raster Attribute Table table: could not add rows." );
577 }
578 return false;
579 }
580
581 result = writer->flushBuffer();
582
583 if ( result )
584 {
585 mFilePath = cleanedPath;
586 setDirty( false );
587 }
588
589 return result;
590}
591
592bool QgsRasterAttributeTable::readFromFile( const QString &path, QString *errorMessage )
593{
594 QgsOgrProvider ratDbfSource { path, QgsDataProvider::ProviderOptions() };
595 if ( !ratDbfSource.isValid() )
596 {
597 if ( errorMessage )
598 {
599 *errorMessage = tr( "Error reading Raster Attribute Table table from file: invalid layer." );
600 }
601 return false;
602 }
603
604 QList<Field> oldFields = mFields;
605 QList<QVariantList> oldData = mData;
606
607 mFields.clear();
608 mData.clear();
609
610 bool hasValueField { false };
611 for ( const QgsField &field : ratDbfSource.fields() )
612 {
613 const Qgis::RasterAttributeTableFieldUsage usage { guessFieldUsage( field.name(), field.type() ) };
614 QMetaType::Type type { field.type() };
615 // DBF sets all int fields to long but for RGBA it doesn't make sense
616 if ( type == QMetaType::Type::LongLong
618 {
619 type = QMetaType::Type::Int;
620 }
621
623 {
624 hasValueField = true;
625 }
626
627 QgsRasterAttributeTable::Field ratField { field.name(), usage, type };
628 if ( !appendField( ratField, errorMessage ) )
629 {
630 mFields = oldFields;
631 mData = oldData;
632 return false;
633 }
634 }
635
636 // Do we have a value field? If not, try to guess one
637 if ( ! hasValueField && mFields.count() > 1 && ( mFields.at( 0 ).type == QMetaType::Type::Int || mFields.at( 0 ).type == QMetaType::Type::QChar || mFields.at( 0 ).type == QMetaType::Type::UInt || mFields.at( 0 ).type == QMetaType::Type::LongLong || mFields.at( 0 ).type == QMetaType::Type::ULongLong ) )
638 {
640 }
641
642 const int fieldCount { static_cast<int>( ratDbfSource.fields().count() ) };
643 QgsFeature f;
644 QgsFeatureIterator fit { ratDbfSource.getFeatures( QgsFeatureRequest() ) };
645 while ( fit.nextFeature( f ) )
646 {
647 if ( f.attributeCount() != fieldCount )
648 {
649 if ( errorMessage )
650 {
651 *errorMessage = tr( "Error reading Raster Attribute Table table from file: number of fields and number of attributes do not match." );
652 }
653 mFields = oldFields;
654 mData = oldData;
655 return false;
656 }
657 appendRow( f.attributes().toList() );
658 }
659
660 mFilePath = path;
661 setDirty( false );
662
663 return true;
664}
665
666
667bool QgsRasterAttributeTable::isValid( QString *errorMessage ) const
668{
669 QStringList errors;
670
671 if ( mFields.isEmpty() )
672 {
673 errors.push_back( tr( "The attribute table has no fields." ) );
674 }
675
676 if ( mData.isEmpty() )
677 {
678 errors.push_back( tr( "The attribute table has no rows." ) );
679 }
680
681 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
682 const bool isMinMax { fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) };
683 const bool isValueRamp { fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Min ) && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Max ) };
684 if ( !isMinMax && !isValueRamp )
685 {
686 errors.push_back( tr( "The attribute table has no MinMax nor a pair of Min and Max fields." ) );
687 }
688
689 // Check color
690 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Red )
691 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Green )
692 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Blue ) )
693 {
694 if ( !( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Red )
695 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Green )
696 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Blue ) ) )
697 {
698 errors.push_back( tr( "The attribute table has some but not all the fields required for color definition (Red, Green, Blue)." ) );
699 }
700 }
701
702 // Check ramp
703 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMin )
704 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMin )
705 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMin )
706 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMax )
707 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMax )
708 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMax ) )
709 {
710 if ( !( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMin )
711 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMin )
712 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMin )
713 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMax )
714 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMax )
715 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMax ) ) )
716 {
717 errors.push_back( tr( "The attribute table has some but not all the fields required for color ramp definition (RedMin, GreenMin, BlueMin, RedMax, GreenMax, BlueMax)." ) );
718 }
719 else if ( !isValueRamp )
720 {
721 errors.push_back( tr( "The attribute table has all the fields required for color ramp definition (RedMin, GreenMin, BlueMin, RedMax, GreenMax, BlueMax) but no Min and Max field." ) );
722 }
723 }
724
725 if ( errorMessage && !errors.isEmpty() )
726 {
727 *errorMessage = errors.join( QChar( '\n' ) );
728 }
729
730 return errors.isEmpty();
731}
732
733const QList<QList<QVariant> > QgsRasterAttributeTable::data() const
734{
735 return mData;
736}
737
739{
740 for ( const Field &f : std::as_const( mFields ) )
741 {
742 if ( f.name == name )
743 {
744 if ( ok )
745 {
746 *ok = true;
747 }
748 return f;
749 }
750 }
751 if ( ok )
752 {
753 *ok = false;
754 }
755 return Field( QString(), Qgis::RasterAttributeTableFieldUsage::Generic, QMetaType::Type::QString );
756}
757
758const QList<QgsRasterAttributeTable::Field> QgsRasterAttributeTable::fieldsByUsage( const Qgis::RasterAttributeTableFieldUsage fieldUsage ) const
759{
760 QList<QgsRasterAttributeTable::Field> result;
761 for ( const Field &f : std::as_const( mFields ) )
762 {
763 if ( f.usage == fieldUsage )
764 {
765 result.push_back( f );
766 }
767 }
768 return result;
769}
770
771bool QgsRasterAttributeTable::setValue( const int row, const int column, const QVariant &value )
772{
773 if ( row < 0 || row >= mData.count() || column < 0 || column >= mData[row].count() )
774 {
775 return false;
776 }
777
778 QVariant newVal = value;
779 if ( column >= mFields.length() || !value.canConvert( mFields.at( column ).type ) || !newVal.convert( mFields.at( column ).type ) )
780 {
781 return false;
782 }
783
784 const QVariant oldVal = mData[row][column];
785
786 if ( newVal != oldVal )
787 {
788 mData[row][column] = newVal;
789 setDirty( true );
790 }
791
792 return true;
793}
794
795QVariant QgsRasterAttributeTable::value( const int row, const int column ) const
796{
797 if ( row < 0 || row >= mData.count() || column < 0 || column >= mData[row].count() )
798 {
799 return QVariant();
800 }
801 return mData[row][column];
802}
803
805{
806 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
807 bool ok { false };
808 int fieldIdx { -1 };
809
810 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
811 {
812 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) );
813 }
814 else if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Min ) )
815 {
816 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) );
817 }
818
819 double min { std::numeric_limits<double>::max() };
820 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
821 {
822 min = std::min( min, value( rowIdx, fieldIdx ).toDouble( &ok ) );
823 if ( !ok )
824 {
825 return std::numeric_limits<double>::quiet_NaN();
826 }
827 }
828
829 if ( fieldIdx == -1 || !ok )
830 {
831 return std::numeric_limits<double>::quiet_NaN();
832 }
833 else
834 {
835 return min;
836 }
837}
838
840{
841 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
842 bool ok { false };
843 int fieldIdx { -1 };
844
845 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
846 {
847 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) );
848 }
849 else if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Max ) )
850 {
851 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) );
852 }
853
854 double max { std::numeric_limits<double>::lowest() };
855 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
856 {
857 max = std::max( max, value( rowIdx, fieldIdx ).toDouble( &ok ) );
858 if ( !ok )
859 {
860 return std::numeric_limits<double>::quiet_NaN();
861 }
862 }
863
864 if ( fieldIdx == -1 || !ok )
865 {
866 return std::numeric_limits<double>::quiet_NaN();
867 }
868 else
869 {
870 return max;
871 }
872}
873
874QVariantList QgsRasterAttributeTable::row( const double matchValue ) const
875{
876 if ( !isValid() )
877 {
878 return QVariantList();
879 }
880
881 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
882
883 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
884 {
885 const int colIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) ) };
886 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
887 {
888 bool ok;
889 if ( matchValue == value( rowIdx, colIdx ).toDouble( &ok ) && ok )
890 {
891 return mData.at( rowIdx );
892 }
893 }
894 }
895 else
896 {
897 const int minColIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) ) };
898 const int maxColIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) ) };
899 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
900 {
901 bool ok;
902 if ( matchValue >= value( rowIdx, minColIdx ).toDouble( &ok ) && ok )
903 {
904 if ( matchValue < value( rowIdx, maxColIdx ).toDouble( &ok ) && ok )
905 {
906 return mData.at( rowIdx );
907 }
908 }
909 }
910 }
911 return QVariantList();
912}
913
915{
916 static const QStringList minValueNames { {
917 u"min"_s,
918 u"min_value"_s,
919 u"min value"_s,
920 u"value min"_s,
921 u"value_min"_s,
922 } };
923
924 static const QStringList maxValueNames { {
925 u"max"_s,
926 u"max_value"_s,
927 u"max value"_s,
928 u"value max"_s,
929 u"value_max"_s,
930 } };
931
932 const QString fieldLower { name.toLower() };
933
934 if ( type == QMetaType::Type::Double || type == QMetaType::Type::Int || type == QMetaType::Type::UInt || type == QMetaType::Type::LongLong || type == QMetaType::Type::ULongLong )
935 {
936 if ( minValueNames.contains( fieldLower ) )
937 {
939 }
940 else if ( maxValueNames.contains( fieldLower ) )
941 {
943 }
944 else if ( fieldLower == "value"_L1 )
945 {
947 }
948 else if ( fieldLower == "count"_L1 )
949 {
950 // This could really be max count but it's more likely pixel count
952 }
953 // Colors (not double)
954 else if ( type != QMetaType::Type::Double )
955 {
956 if ( fieldLower.contains( "red" ) || fieldLower == "r"_L1 )
957 {
958 if ( fieldLower.contains( "min" ) )
959 {
961 }
962 else if ( fieldLower.contains( "max" ) )
963 {
965 }
966 else
967 {
969 }
970 }
971 else if ( fieldLower.contains( "green" ) || fieldLower == "g"_L1 )
972 {
973 if ( fieldLower.contains( "min" ) )
974 {
976 }
977 else if ( fieldLower.contains( "max" ) )
978 {
980 }
981 else
982 {
984 }
985 }
986 else if ( fieldLower.contains( "blue" ) || fieldLower == "b"_L1 )
987 {
988 if ( fieldLower.contains( "min" ) )
989 {
991 }
992 else if ( fieldLower.contains( "max" ) )
993 {
995 }
996 else
997 {
999 }
1000 }
1001 else if ( fieldLower.contains( "alpha" ) || fieldLower == "a"_L1 )
1002 {
1003 if ( fieldLower.contains( "min" ) )
1004 {
1006 }
1007 else if ( fieldLower.contains( "max" ) )
1008 {
1010 }
1011 else
1012 {
1014 }
1015 }
1016 }
1017 // end colors
1018 }
1019
1020 if ( type == QMetaType::Type::QString ) // default to name for strings
1021 {
1023 }
1024
1025 // default to generic for all other cases
1027}
1028
1033
1035{
1036 switch ( usage )
1037 {
1039 return tr( "Red" );
1041 return tr( "Green" );
1043 return tr( "Blue" );
1045 return tr( "Alpha" );
1047 return tr( "Red Minimum" );
1049 return tr( "Green Minimum" );
1051 return tr( "Blue Minimum" );
1053 return tr( "Alpha Minimum" );
1055 return tr( "Red Maximum" );
1057 return tr( "Green Maximum" );
1059 return tr( "Blue Maximum" );
1061 return tr( "Alpha Maximum" );
1063 return tr( "Generic" );
1065 return tr( "Name" );
1067 return tr( "Pixel Count" );
1069 return tr( "Maximum Count" );
1071 return tr( "Value" );
1073 return tr( "Minimum Value" );
1075 return tr( "Maximum Value" );
1076 }
1077 return QString();
1078}
1079
1101
1103{
1104 if ( !raster || !raster->dataProvider() || !raster->isValid() )
1105 {
1106 return nullptr;
1107 }
1108
1109 const QgsRasterRenderer *renderer = raster->renderer();
1110
1111 if ( !renderer )
1112 {
1113 return nullptr;
1114 }
1115
1116 if ( const QgsPalettedRasterRenderer *palettedRenderer = dynamic_cast<const QgsPalettedRasterRenderer *>( renderer ) )
1117 {
1119 rat->appendField( u"Value"_s, Qgis::RasterAttributeTableFieldUsage::MinMax, QMetaType::Type::Double );
1120 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1121 rat->appendField( u"Red"_s, Qgis::RasterAttributeTableFieldUsage::Red, QMetaType::Type::Int );
1122 rat->appendField( u"Green"_s, Qgis::RasterAttributeTableFieldUsage::Green, QMetaType::Type::Int );
1123 rat->appendField( u"Blue"_s, Qgis::RasterAttributeTableFieldUsage::Blue, QMetaType::Type::Int );
1124 rat->appendField( u"Alpha"_s, Qgis::RasterAttributeTableFieldUsage::Alpha, QMetaType::Type::Int );
1125
1126 const QgsPalettedRasterRenderer::ClassData classes { palettedRenderer->classes() };
1127
1128 for ( const QgsPalettedRasterRenderer::Class &klass : std::as_const( classes ) )
1129 {
1130 rat->appendRow( QVariantList() << klass.value << klass.label << 0 << 0 << 0 << 255 );
1131 rat->setColor( static_cast<int>( rat->data().length() - 1 ), klass.color );
1132 }
1133
1134 if ( bandNumber )
1135 {
1136 *bandNumber = palettedRenderer->inputBand();
1137 }
1138 return rat;
1139 }
1140 else if ( const QgsSingleBandPseudoColorRenderer *pseudoColorRenderer = dynamic_cast<const QgsSingleBandPseudoColorRenderer *>( renderer ) )
1141 {
1142 if ( const QgsRasterShader *shader = pseudoColorRenderer->shader() )
1143 {
1144 if ( const QgsColorRampShader *shaderFunction = dynamic_cast<const QgsColorRampShader *>( shader->rasterShaderFunction() ) )
1145 {
1147 switch ( shaderFunction->colorRampType() )
1148 {
1150 {
1151 rat->appendField( u"Min"_s, Qgis::RasterAttributeTableFieldUsage::Min, QMetaType::Type::Double );
1152 rat->appendField( u"Max"_s, Qgis::RasterAttributeTableFieldUsage::Max, QMetaType::Type::Double );
1153 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1154 rat->appendField( u"RedMin"_s, Qgis::RasterAttributeTableFieldUsage::RedMin, QMetaType::Type::Int );
1155 rat->appendField( u"GreenMin"_s, Qgis::RasterAttributeTableFieldUsage::GreenMin, QMetaType::Type::Int );
1156 rat->appendField( u"BlueMin"_s, Qgis::RasterAttributeTableFieldUsage::BlueMin, QMetaType::Type::Int );
1157 rat->appendField( u"AlphaMin"_s, Qgis::RasterAttributeTableFieldUsage::AlphaMin, QMetaType::Type::Int );
1158 rat->appendField( u"RedMax"_s, Qgis::RasterAttributeTableFieldUsage::RedMax, QMetaType::Type::Int );
1159 rat->appendField( u"GreenMax"_s, Qgis::RasterAttributeTableFieldUsage::GreenMax, QMetaType::Type::Int );
1160 rat->appendField( u"BlueMax"_s, Qgis::RasterAttributeTableFieldUsage::BlueMax, QMetaType::Type::Int );
1161 rat->appendField( u"AlphaMax"_s, Qgis::RasterAttributeTableFieldUsage::AlphaMax, QMetaType::Type::Int );
1162 const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
1163 if ( rampItems.size() > 1 )
1164 {
1165 QColor color1 { rampItems.at( 0 ).color };
1166 QString label1 { rampItems.at( 0 ).label };
1167 QVariant value1( rampItems.at( 0 ).value );
1168 const int rampItemSize = static_cast<int>( rampItems.size() );
1169 for ( int i = 1; i < rampItemSize; ++i )
1170 {
1171 const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i ) };
1172 rat->appendRow( QVariantList() << value1 << rampItem.value << u"%1 - %2"_s.arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
1173 rat->setRamp( static_cast<int>( rat->data().length() - 1 ), color1, rampItem.color );
1174 label1 = rampItem.label;
1175 value1 = rampItem.value;
1176 color1 = rampItem.color;
1177 }
1178 }
1179 break;
1180 }
1181
1183 {
1184 rat->appendField( u"Min"_s, Qgis::RasterAttributeTableFieldUsage::Min, QMetaType::Type::Double );
1185 rat->appendField( u"Max"_s, Qgis::RasterAttributeTableFieldUsage::Max, QMetaType::Type::Double );
1186 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1187 rat->appendField( u"Red"_s, Qgis::RasterAttributeTableFieldUsage::Red, QMetaType::Type::Int );
1188 rat->appendField( u"Green"_s, Qgis::RasterAttributeTableFieldUsage::Green, QMetaType::Type::Int );
1189 rat->appendField( u"Blue"_s, Qgis::RasterAttributeTableFieldUsage::Blue, QMetaType::Type::Int );
1190 rat->appendField( u"Alpha"_s, Qgis::RasterAttributeTableFieldUsage::Alpha, QMetaType::Type::Int );
1191 const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
1192 if ( rampItems.size() > 1 )
1193 {
1194 QColor color1 { rampItems.at( 0 ).color };
1195 QString label1 { rampItems.at( 0 ).label };
1196 QVariant value1( rampItems.at( 0 ).value );
1197 const int rampItemSize = static_cast<int>( rampItems.size() );
1198 for ( int i = 1; i < rampItemSize; ++i )
1199 {
1200 const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i ) };
1201 rat->appendRow( QVariantList() << value1 << rampItem.value << u"%1 - %2"_s.arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
1202 rat->setRamp( static_cast<int>( rat->data().length() - 1 ), color1, rampItem.color );
1203 label1 = rampItem.label;
1204 value1 = rampItem.value;
1205 color1 = rampItem.color;
1206 }
1207 }
1208 break;
1209 }
1210
1212 {
1213 rat->appendField( u"Value"_s, Qgis::RasterAttributeTableFieldUsage::MinMax, QMetaType::Type::Double );
1214 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1215 rat->appendField( u"Red"_s, Qgis::RasterAttributeTableFieldUsage::Red, QMetaType::Type::Int );
1216 rat->appendField( u"Green"_s, Qgis::RasterAttributeTableFieldUsage::Green, QMetaType::Type::Int );
1217 rat->appendField( u"Blue"_s, Qgis::RasterAttributeTableFieldUsage::Blue, QMetaType::Type::Int );
1218 rat->appendField( u"Alpha"_s, Qgis::RasterAttributeTableFieldUsage::Alpha, QMetaType::Type::Int );
1219 const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
1220 for ( const QgsColorRampShader::ColorRampItem &rampItem : std::as_const( rampItems ) )
1221 {
1222 rat->appendRow( QVariantList() << rampItem.value << rampItem.label << 0 << 0 << 0 << 255 );
1223 rat->setColor( static_cast<int>( rat->data().length() - 1 ), rampItem.color );
1224 }
1225 break;
1226 }
1227 }
1228
1229 if ( bandNumber )
1230 {
1231 *bandNumber = pseudoColorRenderer->inputBand();
1232 }
1233
1234 return rat;
1235 }
1236 else
1237 {
1238 return nullptr;
1239 }
1240 }
1241 else
1242 {
1243 return nullptr;
1244 }
1245 }
1246 else
1247 {
1248 return nullptr;
1249 }
1250}
1251
1252QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> QgsRasterAttributeTable::usageInformation()
1253{
1254 std::call_once( usageInformationLoaderFlag, [] {
1255 QgsRasterAttributeTable::sUsageInformation.insert(
1257 { tr( "General Purpose Field" ), false, false, false, false, true, true, QList<QMetaType::Type>() << QMetaType::Type::QString << QMetaType::Type::Int << QMetaType::Type::LongLong << QMetaType::Type::Double }
1258 );
1259 QgsRasterAttributeTable::sUsageInformation
1260 .insert( Qgis::RasterAttributeTableFieldUsage::PixelCount, { tr( "Histogram Pixel Count" ), true, false, false, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::LongLong } );
1261 QgsRasterAttributeTable::sUsageInformation
1262 .insert( Qgis::RasterAttributeTableFieldUsage::Name, { tr( "Class Name" ), false, false, false, false, true, true, QList<QMetaType::Type>() << QMetaType::Type::QString } );
1263 QgsRasterAttributeTable::sUsageInformation
1264 .insert( Qgis::RasterAttributeTableFieldUsage::MinMax, { tr( "Class Value (min=max)" ), true, true, false, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int << QMetaType::Type::LongLong << QMetaType::Type::Double } );
1265 QgsRasterAttributeTable::sUsageInformation
1266 .insert( Qgis::RasterAttributeTableFieldUsage::Min, { tr( "Class Minimum Value" ), true, true, false, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int << QMetaType::Type::LongLong << QMetaType::Type::Double } );
1267 QgsRasterAttributeTable::sUsageInformation
1268 .insert( Qgis::RasterAttributeTableFieldUsage::Max, { tr( "Class Maximum Value" ), true, true, false, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int << QMetaType::Type::LongLong << QMetaType::Type::Double } );
1269 QgsRasterAttributeTable::sUsageInformation
1270 .insert( Qgis::RasterAttributeTableFieldUsage::Red, { tr( "Red Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1271 QgsRasterAttributeTable::sUsageInformation
1272 .insert( Qgis::RasterAttributeTableFieldUsage::Green, { tr( "Green Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1273 QgsRasterAttributeTable::sUsageInformation
1274 .insert( Qgis::RasterAttributeTableFieldUsage::Blue, { tr( "Blue Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1275 QgsRasterAttributeTable::sUsageInformation
1276 .insert( Qgis::RasterAttributeTableFieldUsage::Alpha, { tr( "Alpha Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1277 QgsRasterAttributeTable::sUsageInformation
1278 .insert( Qgis::RasterAttributeTableFieldUsage::RedMin, { tr( "Red Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1279 QgsRasterAttributeTable::sUsageInformation
1280 .insert( Qgis::RasterAttributeTableFieldUsage::GreenMin, { tr( "Green Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1281 QgsRasterAttributeTable::sUsageInformation
1282 .insert( Qgis::RasterAttributeTableFieldUsage::BlueMin, { tr( "Blue Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1283 QgsRasterAttributeTable::sUsageInformation
1284 .insert( Qgis::RasterAttributeTableFieldUsage::AlphaMin, { tr( "Alpha Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1285 QgsRasterAttributeTable::sUsageInformation
1286 .insert( Qgis::RasterAttributeTableFieldUsage::RedMax, { tr( "Red Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1287 QgsRasterAttributeTable::sUsageInformation
1288 .insert( Qgis::RasterAttributeTableFieldUsage::GreenMax, { tr( "Green Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1289 QgsRasterAttributeTable::sUsageInformation
1290 .insert( Qgis::RasterAttributeTableFieldUsage::BlueMax, { tr( "Blue Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1291 QgsRasterAttributeTable::sUsageInformation
1292 .insert( Qgis::RasterAttributeTableFieldUsage::AlphaMax, { tr( "Alpha Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1293 // Unsupported!!
1294 QgsRasterAttributeTable::sUsageInformation
1295 .insert( Qgis::RasterAttributeTableFieldUsage::MaxCount, { tr( "Maximum GFU value(equals to GFU_AlphaMax+1 currently)" ), true, false, false, true, false, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1296 } );
1297 return QgsRasterAttributeTable::sUsageInformation;
1298}
1299
1300void QgsRasterAttributeTable::setType()
1301{
1302 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1304}
1305
1306
1308QHash<int, QgsRasterAttributeTable::UsageInformation> QgsRasterAttributeTable::usageInformationInt()
1309{
1310 QHash<int, QgsRasterAttributeTable::UsageInformation> usageInfoInt;
1311 const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
1312 for ( auto it = usageInfo.cbegin(); it != usageInfo.cend(); ++it )
1313 {
1314 usageInfoInt.insert( static_cast<int>( it.key() ), it.value() );
1315 }
1316 return usageInfoInt;
1317}
1319
1321{
1322 return mFilePath;
1323}
1324
1325QList<QgsRasterAttributeTable::MinMaxClass> QgsRasterAttributeTable::minMaxClasses( const int classificationColumn ) const
1326{
1327 QList<QgsRasterAttributeTable::MinMaxClass> classes;
1328 if ( !isValid() )
1329 {
1330 QgsDebugError( "minMaxClasses was called on an invalid RAT" );
1331 return classes;
1332 }
1333
1334 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1335
1336 if ( !fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
1337 {
1338 QgsDebugError( "minMaxClasses was called on a ramp raster" );
1339 return classes;
1340 }
1341
1342 const int minMaxIndex { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) ) };
1343
1344 Q_ASSERT( minMaxIndex >= 0 );
1345
1346 int classificationIndex = classificationColumn;
1347 if ( classificationIndex >= 0 && classificationIndex < mFields.count() )
1348 {
1349 const Field classificationField { mFields.at( classificationIndex ) };
1350 if ( ( classificationField.usage != Qgis::RasterAttributeTableFieldUsage::Name && classificationField.usage != Qgis::RasterAttributeTableFieldUsage::Generic ) )
1351 {
1352 QgsDebugError( "minMaxClasses was called with a classification column which is not suitable for classification" );
1353 return classes;
1354 }
1355 }
1356 else if ( classificationIndex == -1 ) // Special value for not-set
1357 {
1358 // Find first value or generic field
1359 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Name ) )
1360 {
1361 classificationIndex = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Name ) );
1362 }
1363 else if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Generic ) )
1364 {
1365 classificationIndex = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Generic ) );
1366 }
1367 else
1368 {
1369 classificationIndex = minMaxIndex;
1370 }
1371 }
1372 else if ( classificationIndex >= mFields.count() )
1373 {
1374 QgsDebugError( "minMaxClasses was called with a classification column out of range" );
1375 return classes;
1376 }
1377
1378 if ( classificationIndex >= 0 )
1379 {
1380 QStringList labels;
1381 int rowIdx { 0 };
1382 for ( const QVariantList &row : std::as_const( mData ) )
1383 {
1384 const QString label { row.at( classificationIndex ).toString() };
1385 bool ok;
1386 const double value { row.at( minMaxIndex ).toDouble( &ok ) };
1387 // This should never happen, could eventually become a Q_ASSERT
1388 if ( !ok )
1389 {
1390 QgsDebugError( "minMaxClasses could not convert a MinMax value to double" );
1391 return classes;
1392 }
1393 if ( labels.contains( label ) )
1394 {
1395 classes[labels.indexOf( label )].minMaxValues.push_back( value );
1396 }
1397 else
1398 {
1399 labels.push_back( label );
1400 classes.push_back( { label, { value }, color( rowIdx ) } );
1401 }
1402 rowIdx++;
1403 }
1404 }
1405 return classes;
1406}
1407
1408QgsGradientColorRamp QgsRasterAttributeTable::colorRamp( QStringList &labels, const int labelColumn ) const
1409{
1410 QgsGradientColorRamp ramp { Qt::GlobalColor::white, Qt::GlobalColor::black };
1411 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1412 const int minIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) ) };
1413 const int maxIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) ) };
1414 const bool isRange { minIdx >= 0 && maxIdx >= 0 };
1415
1416 int labelIdx { labelColumn };
1417 if ( labelColumn < 0
1418 || labelColumn >= fields().count()
1419 || ( fieldUsages.at( labelColumn ) != Qgis::RasterAttributeTableFieldUsage::Name && fieldUsages.at( labelColumn ) != Qgis::RasterAttributeTableFieldUsage::Generic ) )
1420 {
1421 labelIdx = -1;
1422 }
1423
1424 if ( !mData.isEmpty() && ( minIdx >= 0 && maxIdx >= 0 ) )
1425 {
1427 const bool hasColorOrRamp { hasColor() || hasRamp() };
1428 {
1429 const double min { minimumValue() };
1430 const double max { maximumValue() };
1431 const double range { max - min };
1432
1433 if ( range != 0 )
1434 {
1435 if ( !std::isnan( min ) && !std::isnan( max ) )
1436 {
1437 const QList<QVariantList> dataCopy( orderedRows() );
1438
1439 QgsRasterAttributeTable orderedRat;
1440 for ( const Field &f : std::as_const( mFields ) )
1441 {
1442 orderedRat.appendField( f );
1443 }
1444 for ( const QVariantList &r : std::as_const( dataCopy ) )
1445 {
1446 orderedRat.appendRow( r );
1447 }
1448
1449 QColor lastColor { ramp.color1() };
1450
1451 if ( hasColorOrRamp )
1452 {
1453 ramp.setColor1( orderedRat.hasColor() ? orderedRat.color( 0 ) : orderedRat.ramp( 0 ).color1() );
1454 ramp.setColor2( orderedRat.hasColor() ? orderedRat.color( static_cast<int>( orderedRat.data().count() - 1 ) ) : orderedRat.ramp( static_cast<int>( orderedRat.data().count() - 1 ) ).color2() );
1455 lastColor = orderedRat.hasColor() ? orderedRat.color( 0 ) : orderedRat.ramp( 0 ).color2();
1456 }
1457
1458 auto labelFromField = [&]( int rowIdx ) -> QString {
1459 if ( labelIdx < 0 )
1460 {
1461 return u"%L1 - %L2"_s.arg( orderedRat.value( rowIdx, minIdx ).toDouble() ).arg( orderedRat.value( rowIdx, maxIdx ).toDouble() );
1462 }
1463 const QVariant val( orderedRat.value( rowIdx, labelIdx ) );
1464 bool ok { true };
1465 QString res;
1466 switch ( val.userType() )
1467 {
1468 case QMetaType::Type::QChar:
1469 return QString( val.toChar() );
1470 case QMetaType::Type::Int:
1471 res = QLocale().toString( val.toInt( &ok ) );
1472 break;
1473 case QMetaType::Type::LongLong:
1474 res = QLocale().toString( val.toLongLong( &ok ) );
1475 break;
1476 case QMetaType::Type::UInt:
1477 res = QLocale().toString( val.toUInt( &ok ) );
1478 break;
1479 case QMetaType::Type::ULongLong:
1480 res = QLocale().toString( val.toULongLong( &ok ) );
1481 break;
1482 case QMetaType::Type::Double:
1483 res = QLocale().toString( val.toDouble( &ok ), 'g' );
1484 break;
1485 case QMetaType::Type::QString:
1486 default:
1487 return val.toString();
1488 }
1489 return ok ? res : val.toString();
1490 };
1491
1492 // Case 1: range classes, discrete colors
1493 // - create stops for the lower value of each class except for the first,
1494 // - use the color from the previous class
1495 if ( orderedRat.hasColor() && isRange )
1496 {
1497 labels.push_back( labelFromField( 0 ) );
1498
1499 for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
1500 {
1501 const double offset { ( orderedRat.value( rowIdx, minIdx ).toDouble() - min ) / range };
1502 const QColor color { orderedRat.color( rowIdx - 1 ) };
1503 stops.append( QgsGradientStop( offset, color ) );
1504 labels.push_back( labelFromField( rowIdx ) );
1505 }
1506 }
1507 // Case 2: range classes, gradients colors
1508 // Take the class bounds (average value between max of previous class and min of the next)
1509 // to avoid potential overlapping or gaps between classes.
1510 // Create stop:
1511 // - first stop at value taking the max color of the previous class
1512 // - second stop at value + epsilon taking the min color of the next class, unless colors and offset are equal
1513 else if ( orderedRat.hasRamp() && isRange )
1514 {
1515 double prevOffset { 0 };
1516 labels.push_back( labelFromField( 0 ) );
1517 for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
1518 {
1519 labels.push_back( labelFromField( rowIdx ) );
1520 const int prevRowIdx { rowIdx - 1 };
1521 const double offset { ( ( orderedRat.value( rowIdx, minIdx ).toDouble() + orderedRat.value( prevRowIdx, maxIdx ).toDouble() ) / 2.0 - min ) / range };
1522 const QgsGradientColorRamp previousRamp { orderedRat.ramp( prevRowIdx ) };
1523 stops.append( QgsGradientStop( offset, previousRamp.color2() ) );
1524
1525 const QgsGradientColorRamp currentRamp { orderedRat.ramp( rowIdx ) };
1526 // An additional stop is added if the colors are different or offsets are different by 1e-6 (offset varies from 0 to 1).
1527 if ( currentRamp.color1() != previousRamp.color2() && qgsDoubleNear( offset, prevOffset, 1e-6 ) )
1528 {
1529 stops.append( QgsGradientStop( offset + std::numeric_limits<double>::epsilon(), currentRamp.color1() ) );
1530 }
1531 prevOffset = offset;
1532 }
1533 }
1534 // Case 3: range classes but no colors at all
1535 // Take the class borders (average value between max of previous class and min of the next)
1536 // Create stop for the lower class, actually skipping the upper bound of the last class
1537 else
1538 {
1539 labels.push_back( labelFromField( 0 ) );
1540
1541 for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
1542 {
1543 const int prevRowIdx { rowIdx - 1 };
1544 const double offset { ( ( orderedRat.value( rowIdx, minIdx ).toDouble() + orderedRat.value( prevRowIdx, maxIdx ).toDouble() ) / 2.0 - min ) / range };
1545 stops.append( QgsGradientStop( offset, ramp.color( offset ) ) );
1546 labels.push_back( labelFromField( rowIdx ) );
1547 }
1548 }
1549 }
1550 }
1551 }
1552
1553 ramp.setStops( stops );
1554 ramp.setDiscrete( hasColor() );
1555 }
1556
1557 return ramp;
1558}
1559
1560QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvider *provider, const int bandNumber, const int classificationColumn )
1561{
1562 if ( !provider )
1563 {
1564 return nullptr;
1565 }
1566
1567 std::unique_ptr<QgsRasterRenderer> renderer;
1568
1570 {
1571 std::unique_ptr<QgsColorRamp> ramp;
1572 if ( !hasColor() )
1573 {
1574 ramp = std::make_unique<QgsRandomColorRamp>();
1575 }
1577 if ( classes.isEmpty() )
1578 return nullptr;
1579
1580 renderer = std::make_unique<QgsPalettedRasterRenderer>( provider, bandNumber, classes );
1581 }
1582 else
1583 {
1584 auto pseudoColorRenderer = std::make_unique<QgsSingleBandPseudoColorRenderer>( provider, bandNumber );
1585 QStringList labels;
1586 // Athematic classification is not supported, but the classificationColumn will be used for labels.
1587 // if it's not specified, try to guess it here.
1588 int labelColumn { classificationColumn };
1589 if ( labelColumn < 0 )
1590 {
1591 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1592 labelColumn = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Name ) );
1593 if ( labelColumn < 0 )
1594 {
1595 labelColumn = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Generic ) );
1596 }
1597 }
1598 QgsGradientColorRamp *ramp { colorRamp( labels, labelColumn ).clone() };
1599 pseudoColorRenderer->setClassificationMin( minimumValue() );
1600 pseudoColorRenderer->setClassificationMax( maximumValue() );
1601 // Use discrete for single colors, interpolated for ramps
1602 pseudoColorRenderer->createShader(
1604 );
1605 if ( pseudoColorRenderer->shader() )
1606 {
1607 pseudoColorRenderer->shader()->setMaximumValue( maximumValue() );
1608 pseudoColorRenderer->shader()->setMinimumValue( minimumValue() );
1609 // Set labels
1610 if ( QgsColorRampShader *shaderFunction = static_cast<QgsColorRampShader *>( pseudoColorRenderer->shader()->rasterShaderFunction() ) )
1611 {
1612 shaderFunction->setMinimumValue( minimumValue() );
1613 shaderFunction->setMaximumValue( maximumValue() );
1614 const bool labelsAreUsable { ramp->count() > 2 && labels.count() == ramp->count() - 1 };
1615
1616 if ( labelsAreUsable )
1617 {
1618 QList<QgsColorRampShader::ColorRampItem> newItemList;
1619 const double range { maximumValue() - minimumValue() };
1620 int stopIdx { 0 };
1621 for ( const QString &label : std::as_const( labels ) )
1622 {
1623 if ( stopIdx >= ramp->count() - 2 )
1624 {
1625 break;
1626 }
1627 double value { minimumValue() + ramp->stops().at( stopIdx ).offset * range };
1628 QgsColorRampShader::ColorRampItem item { value, ramp->stops().at( stopIdx ).color, label };
1629 newItemList.push_back( item );
1630 stopIdx++;
1631 }
1632
1633 QgsColorRampShader::ColorRampItem item { maximumValue(), ramp->color2(), labels.last() };
1634 newItemList.push_back( item );
1635 shaderFunction->setColorRampItemList( newItemList );
1636 }
1637 }
1638 }
1639 renderer = std::move( pseudoColorRenderer );
1640 }
1641
1642 return renderer.release();
1643}
1644
1645QList<QList<QVariant> > QgsRasterAttributeTable::orderedRows() const
1646{
1647 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1648 const int minIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) ) };
1649 const int maxIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) ) };
1650 const bool isRange { minIdx >= 0 && maxIdx >= 0 };
1651 QList<QVariantList> dataCopy( mData );
1652
1653 if ( isRange )
1654 {
1655 std::sort( dataCopy.begin(), dataCopy.end(), [&]( const QVariantList &first, const QVariantList &second ) -> bool {
1656 return ( first.at( maxIdx ).toDouble() + first.at( minIdx ).toDouble() ) < ( second.at( maxIdx ).toDouble() + second.at( minIdx ).toDouble() );
1657 } );
1658 }
1659 else
1660 {
1661 const int minMaxIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) ) };
1662 if ( minMaxIdx < 0 )
1663 {
1664 return dataCopy;
1665 }
1666 else
1667 {
1668 std::sort( dataCopy.begin(), dataCopy.end(), [&]( const QVariantList &first, const QVariantList &second ) -> bool { return first.at( minMaxIdx ).toDouble() < second.at( minMaxIdx ).toDouble(); } );
1669 }
1670 }
1671
1672 return dataCopy;
1673}
1674
1682
@ Exact
Assigns the color of the exact matching value in the color ramp item list.
Definition qgis.h:1548
@ Linear
Interpolates the color between two class breaks linearly.
Definition qgis.h:1546
@ Discrete
Assigns the color of the higher class for every pixel between two class breaks.
Definition qgis.h:1547
RasterAttributeTableType
The RasterAttributeTableType enum represents the type of RAT.
Definition qgis.h:1737
@ Continuous
Uses breaks from color palette.
Definition qgis.h:1561
RasterAttributeTableFieldUsage
The RasterAttributeTableFieldUsage enum represents the usage of a Raster Attribute Table field.
Definition qgis.h:1708
@ RedMax
Field usage RedMax.
Definition qgis.h:1723
@ PixelCount
Field usage PixelCount.
Definition qgis.h:1710
@ AlphaMax
Field usage AlphaMax.
Definition qgis.h:1726
@ BlueMin
Field usage BlueMin.
Definition qgis.h:1721
@ Alpha
Field usage Alpha.
Definition qgis.h:1718
@ Generic
Field usage Generic.
Definition qgis.h:1709
@ GreenMax
Field usage GreenMax.
Definition qgis.h:1724
@ RedMin
Field usage RedMin.
Definition qgis.h:1719
@ MinMax
Field usage MinMax.
Definition qgis.h:1714
@ BlueMax
Field usage BlueMax.
Definition qgis.h:1725
@ AlphaMin
Field usage AlphaMin.
Definition qgis.h:1722
@ Green
Field usage Green.
Definition qgis.h:1716
@ MaxCount
Not used by QGIS: GDAL Maximum GFU value (equals to GFU_AlphaMax+1 currently).
Definition qgis.h:1727
@ GreenMin
Field usage GreenMin.
Definition qgis.h:1720
@ NoGeometry
No geometry.
Definition qgis.h:312
A vector of attributes.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsAttributes attributes
Definition qgsfeature.h:64
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
int attributeCount() const
Returns the number of attributes attached to the feature.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
QColor color1() const
Returns the gradient start color.
QgsGradientColorRamp * clone() const override
Creates a clone of the color ramp.
QColor color2() const
Returns the gradient end color.
Represents a color stop within a QgsGradientColorRamp color ramp.
Renderer for paletted raster images.
static QgsPalettedRasterRenderer::MultiValueClassData rasterAttributeTableToClassData(const QgsRasterAttributeTable *attributeTable, int classificationColumn=-1, QgsColorRamp *ramp=nullptr)
Reads and returns classes from the Raster Attribute Table attributeTable, optionally classifying the ...
QList< QgsPalettedRasterRenderer::Class > ClassData
Map of value to class properties.
QList< QgsPalettedRasterRenderer::MultiValueClass > MultiValueClassData
Map of multi value to class properties.
The Field class represents a Raster Attribute Table field, including its name, usage and type.
Qgis::RasterAttributeTableFieldUsage usage
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).
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.
QList< QgsRasterAttributeTable::MinMaxClass > minMaxClasses(const int classificationColumn=-1) const
Returns the classes for a thematic Raster Attribute Table, classified by classificationColumn,...
QgsGradientColorRamp ramp(int row) const
Returns the gradient color ramp of the rat row or a default constructed gradient if row does not exis...
QgsRasterRenderer * createRenderer(QgsRasterDataProvider *provider, const int bandNumber, const int classificationColumn=-1)
Creates and returns a (possibly nullptr) raster renderer for the specified provider and bandNumber an...
bool readFromFile(const QString &path, QString *errorMessage=nullptr)
Reads the Raster Attribute Table from a DBF file specified by path, optionally reporting any error in...
QgsGradientColorRamp colorRamp(QStringList &labels, const int labelColumn=-1) const
Returns the color ramp for an athematic Raster Attribute Table setting the labels in labels,...
bool appendField(const QString &name, const Qgis::RasterAttributeTableFieldUsage usage, const QMetaType::Type type, QString *errorMessage=nullptr)
Creates a new field from name, usage and type and appends it to the fields, optionally reporting any ...
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.
static QList< Qgis::RasterAttributeTableFieldUsage > valueAndColorFieldUsages()
Returns the list of field usages for colors and values.
QList< QgsRasterAttributeTable::Field > fields() const
Returns the Raster Attribute Table fields.
static QgsRasterAttributeTable * createFromRaster(QgsRasterLayer *rasterLayer, int *bandNumber=nullptr)
Creates a new Raster Attribute Table from a raster layer, the renderer must be Paletted or SingleBand...
bool removeRow(int position=0, QString *errorMessage=nullptr)
Removes the row in the Raster Attribute Table at position, optionally reporting any error in errorMes...
bool appendRow(const QVariantList &data, QString *errorMessage=nullptr)
Appends a row of data to the RAT, optionally reporting any error in errorMessage, returns true on suc...
static QHash< Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation > usageInformation()
Returns information about supported Raster Attribute Table usages.
Qgis::RasterAttributeTableType type() const
Returns the Raster Attribute Table type.
QList< Qgis::RasterAttributeTableFieldUsage > usages() const
Returns the list of field usages.
QVariantList row(const double matchValue) const
Returns a row of data for the given matchValue or and empty row if there is not match.
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 writeToFile(const QString &path, QString *errorMessage=nullptr)
Writes the Raster Attribute Table to a DBF file specified by path, optionally reporting any error in ...
double minimumValue() const
Returns the minimum value of the MinMax (thematic) or Min (athematic) column, returns NaN on errors.
double maximumValue() const
Returns the maximum value of the MinMax (thematic) or Max (athematic) column, returns NaN on errors.
const QList< QgsRasterAttributeTable::Field > fieldsByUsage(const Qgis::RasterAttributeTableFieldUsage fieldUsage) const
Returns the list of fields matching fieldUsage.
void setDirty(bool isDirty)
Sets the Raster Attribute Table dirty state to isDirty;.
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...
QList< QList< QVariant > > orderedRows() const
Returns the data rows ordered by the value column(s) in ascending order, if the attribute table type ...
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.
QgsFeatureList qgisFeatures() const
Returns the Raster Attribute Table rows as a list of QgsFeature.
bool setFieldUsage(int fieldIndex, const Qgis::RasterAttributeTableFieldUsage usage)
Change the usage of the field at index fieldIndex to usage with checks for allowed types.
QString filePath() const
Returns the (possibly empty) path of the file-based RAT, the path is set when a RAT is read or writte...
bool insertRamp(int position, QString *errorMessage=nullptr)
Create RGBA minimum and maximum fields and inserts them at position, optionally reporting any error i...
QVariant value(const int row, const int column) const
Returns the value for row and column.
QgsFields qgisFields() const
Returns the Raster Attribute Table fields as QgsFields.
static Qgis::RasterAttributeTableFieldUsage guessFieldUsage(const QString &name, const QMetaType::Type type)
Try to determine the field usage from its name and type.
bool removeField(const QString &name, QString *errorMessage=nullptr)
Removes the field with name, optionally reporting any error in errorMessage, returns true on success.
Base class for raster data providers.
Represents a raster layer.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Raster renderer pipe that applies colors to a raster.
Interface for all raster shaders.
Raster renderer pipe for single band pseudocolor.
static QMetaType::Type variantTypeToMetaType(QVariant::Type variantType)
Converts a QVariant::Type to a QMetaType::Type.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
QStringList layerOptions
List of OGR layer creation options.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
@ CreateOrOverwriteFile
Create or overwrite file.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7340
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
QList< QgsFeature > QgsFeatureList
#define QgsDebugError(str)
Definition qgslogger.h:71
Setting options for creating vector data providers.
Properties of a single value class.