QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 QVariant cell( rowData[idx] );
492 if ( !cell.canConvert( mFields.at( idx ).type ) || !cell.convert( mFields.at( idx ).type ) )
493 {
494 if ( errorMessage )
495 {
496 *errorMessage = tr( "Row data at column %1 cannot be converted to field type (%2)." ).arg( idx ).arg( QVariant::typeToName( mFields.at( idx ).type ) );
497 }
498 return false;
499 }
500 else
501 {
502 dataValid.append( cell );
503 }
504 }
505
506 mData.insert( realPos, dataValid );
507 setDirty( true );
508 return true;
509}
510
511bool QgsRasterAttributeTable::removeRow( int position, QString *errorMessage )
512{
513 if ( position >= mData.count() || position < 0 || mData.isEmpty() )
514 {
515 if ( errorMessage )
516 {
517 *errorMessage = tr( "Position is not valid or the table is empty." );
518 }
519 return false;
520 }
521 mData.removeAt( position );
522 setDirty( true );
523 return true;
524}
525
526bool QgsRasterAttributeTable::appendRow( const QVariantList &data, QString *errorMessage )
527{
528 return insertRow( static_cast<int>( mData.count() ), data, errorMessage );
529}
530
531bool QgsRasterAttributeTable::writeToFile( const QString &path, QString *errorMessage )
532{
535 options.driverName = u"ESRI Shapefile"_s;
536 options.fileEncoding = u"UTF-8"_s;
537 options.layerOptions = QStringList() << u"SHPT=NULL"_s;
538
539 std::unique_ptr<QgsVectorFileWriter> writer;
540
541 // Strip .dbf from path because OGR adds it back
542 QString cleanedPath { path };
543 if ( path.endsWith( u".dbf"_s, Qt::CaseSensitivity::CaseInsensitive ) )
544 {
545 cleanedPath.chop( 4 );
546 }
547
548 cleanedPath = QgsFileUtils::ensureFileNameHasExtension( cleanedPath, { { u".vat"_s } } );
549
551
552 cleanedPath.append( u".dbf"_s );
553
554 const QgsVectorFileWriter::WriterError error { writer->hasError() };
556 {
557 if ( errorMessage )
558 {
559 *errorMessage = tr( "Error creating Raster Attribute Table table: %1." ).arg( writer->errorMessage() );
560 }
561 return false;
562 }
563
564 QgsFeatureList features { qgisFeatures() };
565 bool result { writer->addFeatures( features ) };
566
567 if ( !result )
568 {
569 if ( errorMessage )
570 {
571 *errorMessage = tr( "Error creating Raster Attribute Table table: could not add rows." );
572 }
573 return false;
574 }
575
576 result = writer->flushBuffer();
577
578 if ( result )
579 {
580 mFilePath = cleanedPath;
581 setDirty( false );
582 }
583
584 return result;
585}
586
587bool QgsRasterAttributeTable::readFromFile( const QString &path, QString *errorMessage )
588{
589 QgsOgrProvider ratDbfSource { path, QgsDataProvider::ProviderOptions() };
590 if ( !ratDbfSource.isValid() )
591 {
592 if ( errorMessage )
593 {
594 *errorMessage = tr( "Error reading Raster Attribute Table table from file: invalid layer." );
595 }
596 return false;
597 }
598
599 QList<Field> oldFields = mFields;
600 QList<QVariantList> oldData = mData;
601
602 mFields.clear();
603 mData.clear();
604
605 bool hasValueField { false };
606 for ( const QgsField &field : ratDbfSource.fields() )
607 {
608 const Qgis::RasterAttributeTableFieldUsage usage { guessFieldUsage( field.name(), field.type() ) };
609 QMetaType::Type type { field.type() };
610 // DBF sets all int fields to long but for RGBA it doesn't make sense
611 if ( type == QMetaType::Type::LongLong
613 {
614 type = QMetaType::Type::Int;
615 }
616
618 {
619 hasValueField = true;
620 }
621
622 QgsRasterAttributeTable::Field ratField { field.name(), usage, type };
623 if ( !appendField( ratField, errorMessage ) )
624 {
625 mFields = oldFields;
626 mData = oldData;
627 return false;
628 }
629 }
630
631 // Do we have a value field? If not, try to guess one
632 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 ) )
633 {
635 }
636
637 const int fieldCount { static_cast<int>( ratDbfSource.fields().count() ) };
638 QgsFeature f;
639 QgsFeatureIterator fit { ratDbfSource.getFeatures( QgsFeatureRequest() ) };
640 while ( fit.nextFeature( f ) )
641 {
642 if ( f.attributeCount() != fieldCount )
643 {
644 if ( errorMessage )
645 {
646 *errorMessage = tr( "Error reading Raster Attribute Table table from file: number of fields and number of attributes do not match." );
647 }
648 mFields = oldFields;
649 mData = oldData;
650 return false;
651 }
652 appendRow( f.attributes().toList() );
653 }
654
655 mFilePath = path;
656 setDirty( false );
657
658 return true;
659}
660
661
662bool QgsRasterAttributeTable::isValid( QString *errorMessage ) const
663{
664 QStringList errors;
665
666 if ( mFields.isEmpty() )
667 {
668 errors.push_back( tr( "The attribute table has no fields." ) );
669 }
670
671 if ( mData.isEmpty() )
672 {
673 errors.push_back( tr( "The attribute table has no rows." ) );
674 }
675
676 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
677 const bool isMinMax { fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) };
678 const bool isValueRamp { fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Min ) && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Max ) };
679 if ( !isMinMax && !isValueRamp )
680 {
681 errors.push_back( tr( "The attribute table has no MinMax nor a pair of Min and Max fields." ) );
682 }
683
684 // Check color
685 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Red )
686 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Green )
687 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Blue ) )
688 {
689 if ( !( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Red )
690 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Green )
691 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Blue ) ) )
692 {
693 errors.push_back( tr( "The attribute table has some but not all the fields required for color definition (Red, Green, Blue)." ) );
694 }
695 }
696
697 // Check ramp
698 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMin )
699 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMin )
700 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMin )
701 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMax )
702 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMax )
703 || fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMax ) )
704 {
705 if ( !( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMin )
706 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMin )
707 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMin )
708 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::RedMax )
709 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::GreenMax )
710 && fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::BlueMax ) ) )
711 {
712 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)." ) );
713 }
714 else if ( !isValueRamp )
715 {
716 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." ) );
717 }
718 }
719
720 if ( errorMessage && !errors.isEmpty() )
721 {
722 *errorMessage = errors.join( QChar( '\n' ) );
723 }
724
725 return errors.isEmpty();
726}
727
728const QList<QList<QVariant> > QgsRasterAttributeTable::data() const
729{
730 return mData;
731}
732
734{
735 for ( const Field &f : std::as_const( mFields ) )
736 {
737 if ( f.name == name )
738 {
739 if ( ok )
740 {
741 *ok = true;
742 }
743 return f;
744 }
745 }
746 if ( ok )
747 {
748 *ok = false;
749 }
750 return Field( QString(), Qgis::RasterAttributeTableFieldUsage::Generic, QMetaType::Type::QString );
751}
752
753const QList<QgsRasterAttributeTable::Field> QgsRasterAttributeTable::fieldsByUsage( const Qgis::RasterAttributeTableFieldUsage fieldUsage ) const
754{
755 QList<QgsRasterAttributeTable::Field> result;
756 for ( const Field &f : std::as_const( mFields ) )
757 {
758 if ( f.usage == fieldUsage )
759 {
760 result.push_back( f );
761 }
762 }
763 return result;
764}
765
766bool QgsRasterAttributeTable::setValue( const int row, const int column, const QVariant &value )
767{
768 if ( row < 0 || row >= mData.count() || column < 0 || column >= mData[row].count() )
769 {
770 return false;
771 }
772
773 QVariant newVal = value;
774 if ( column >= mFields.length() || !value.canConvert( mFields.at( column ).type ) || !newVal.convert( mFields.at( column ).type ) )
775 {
776 return false;
777 }
778
779 const QVariant oldVal = mData[row][column];
780
781 if ( newVal != oldVal )
782 {
783 mData[row][column] = newVal;
784 setDirty( true );
785 }
786
787 return true;
788}
789
790QVariant QgsRasterAttributeTable::value( const int row, const int column ) const
791{
792 if ( row < 0 || row >= mData.count() || column < 0 || column >= mData[row].count() )
793 {
794 return QVariant();
795 }
796 return mData[row][column];
797}
798
800{
801 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
802 bool ok { false };
803 int fieldIdx { -1 };
804
805 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
806 {
807 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) );
808 }
809 else if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Min ) )
810 {
811 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) );
812 }
813
814 double min { std::numeric_limits<double>::max() };
815 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
816 {
817 min = std::min( min, value( rowIdx, fieldIdx ).toDouble( &ok ) );
818 if ( !ok )
819 {
820 return std::numeric_limits<double>::quiet_NaN();
821 }
822 }
823
824 if ( fieldIdx == -1 || !ok )
825 {
826 return std::numeric_limits<double>::quiet_NaN();
827 }
828 else
829 {
830 return min;
831 }
832}
833
835{
836 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
837 bool ok { false };
838 int fieldIdx { -1 };
839
840 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
841 {
842 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) );
843 }
844 else if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Max ) )
845 {
846 fieldIdx = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) );
847 }
848
849 double max { std::numeric_limits<double>::lowest() };
850 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
851 {
852 max = std::max( max, value( rowIdx, fieldIdx ).toDouble( &ok ) );
853 if ( !ok )
854 {
855 return std::numeric_limits<double>::quiet_NaN();
856 }
857 }
858
859 if ( fieldIdx == -1 || !ok )
860 {
861 return std::numeric_limits<double>::quiet_NaN();
862 }
863 else
864 {
865 return max;
866 }
867}
868
869QVariantList QgsRasterAttributeTable::row( const double matchValue ) const
870{
871 if ( !isValid() )
872 {
873 return QVariantList();
874 }
875
876 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
877
878 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
879 {
880 const int colIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) ) };
881 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
882 {
883 bool ok;
884 if ( matchValue == value( rowIdx, colIdx ).toDouble( &ok ) && ok )
885 {
886 return mData.at( rowIdx );
887 }
888 }
889 }
890 else
891 {
892 const int minColIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) ) };
893 const int maxColIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) ) };
894 for ( int rowIdx = 0; rowIdx < mData.count(); ++rowIdx )
895 {
896 bool ok;
897 if ( matchValue >= value( rowIdx, minColIdx ).toDouble( &ok ) && ok )
898 {
899 if ( matchValue < value( rowIdx, maxColIdx ).toDouble( &ok ) && ok )
900 {
901 return mData.at( rowIdx );
902 }
903 }
904 }
905 }
906 return QVariantList();
907}
908
910{
911 static const QStringList minValueNames { {
912 u"min"_s,
913 u"min_value"_s,
914 u"min value"_s,
915 u"value min"_s,
916 u"value_min"_s,
917 } };
918
919 static const QStringList maxValueNames { {
920 u"max"_s,
921 u"max_value"_s,
922 u"max value"_s,
923 u"value max"_s,
924 u"value_max"_s,
925 } };
926
927 const QString fieldLower { name.toLower() };
928
929 if ( type == QMetaType::Type::Double || type == QMetaType::Type::Int || type == QMetaType::Type::UInt || type == QMetaType::Type::LongLong || type == QMetaType::Type::ULongLong )
930 {
931 if ( minValueNames.contains( fieldLower ) )
932 {
934 }
935 else if ( maxValueNames.contains( fieldLower ) )
936 {
938 }
939 else if ( fieldLower == "value"_L1 )
940 {
942 }
943 else if ( fieldLower == "count"_L1 )
944 {
945 // This could really be max count but it's more likely pixel count
947 }
948 // Colors (not double)
949 else if ( type != QMetaType::Type::Double )
950 {
951 if ( fieldLower.contains( "red" ) || fieldLower == "r"_L1 )
952 {
953 if ( fieldLower.contains( "min" ) )
954 {
956 }
957 else if ( fieldLower.contains( "max" ) )
958 {
960 }
961 else
962 {
964 }
965 }
966 else if ( fieldLower.contains( "green" ) || fieldLower == "g"_L1 )
967 {
968 if ( fieldLower.contains( "min" ) )
969 {
971 }
972 else if ( fieldLower.contains( "max" ) )
973 {
975 }
976 else
977 {
979 }
980 }
981 else if ( fieldLower.contains( "blue" ) || fieldLower == "b"_L1 )
982 {
983 if ( fieldLower.contains( "min" ) )
984 {
986 }
987 else if ( fieldLower.contains( "max" ) )
988 {
990 }
991 else
992 {
994 }
995 }
996 else if ( fieldLower.contains( "alpha" ) || fieldLower == "a"_L1 )
997 {
998 if ( fieldLower.contains( "min" ) )
999 {
1001 }
1002 else if ( fieldLower.contains( "max" ) )
1003 {
1005 }
1006 else
1007 {
1009 }
1010 }
1011 }
1012 // end colors
1013 }
1014
1015 if ( type == QMetaType::Type::QString ) // default to name for strings
1016 {
1018 }
1019
1020 // default to generic for all other cases
1022}
1023
1028
1030{
1031 switch ( usage )
1032 {
1034 return tr( "Red" );
1036 return tr( "Green" );
1038 return tr( "Blue" );
1040 return tr( "Alpha" );
1042 return tr( "Red Minimum" );
1044 return tr( "Green Minimum" );
1046 return tr( "Blue Minimum" );
1048 return tr( "Alpha Minimum" );
1050 return tr( "Red Maximum" );
1052 return tr( "Green Maximum" );
1054 return tr( "Blue Maximum" );
1056 return tr( "Alpha Maximum" );
1058 return tr( "Generic" );
1060 return tr( "Name" );
1062 return tr( "Pixel Count" );
1064 return tr( "Maximum Count" );
1066 return tr( "Value" );
1068 return tr( "Minimum Value" );
1070 return tr( "Maximum Value" );
1071 }
1072 return QString();
1073}
1074
1096
1098{
1099 if ( !raster || !raster->dataProvider() || !raster->isValid() )
1100 {
1101 return nullptr;
1102 }
1103
1104 const QgsRasterRenderer *renderer = raster->renderer();
1105
1106 if ( !renderer )
1107 {
1108 return nullptr;
1109 }
1110
1111 if ( const QgsPalettedRasterRenderer *palettedRenderer = dynamic_cast<const QgsPalettedRasterRenderer *>( renderer ) )
1112 {
1114 rat->appendField( u"Value"_s, Qgis::RasterAttributeTableFieldUsage::MinMax, QMetaType::Type::Double );
1115 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1116 rat->appendField( u"Red"_s, Qgis::RasterAttributeTableFieldUsage::Red, QMetaType::Type::Int );
1117 rat->appendField( u"Green"_s, Qgis::RasterAttributeTableFieldUsage::Green, QMetaType::Type::Int );
1118 rat->appendField( u"Blue"_s, Qgis::RasterAttributeTableFieldUsage::Blue, QMetaType::Type::Int );
1119 rat->appendField( u"Alpha"_s, Qgis::RasterAttributeTableFieldUsage::Alpha, QMetaType::Type::Int );
1120
1121 const QgsPalettedRasterRenderer::ClassData classes { palettedRenderer->classes() };
1122
1123 for ( const QgsPalettedRasterRenderer::Class &klass : std::as_const( classes ) )
1124 {
1125 rat->appendRow( QVariantList() << klass.value << klass.label << 0 << 0 << 0 << 255 );
1126 rat->setColor( static_cast<int>( rat->data().length() - 1 ), klass.color );
1127 }
1128
1129 if ( bandNumber )
1130 {
1131 *bandNumber = palettedRenderer->inputBand();
1132 }
1133 return rat;
1134 }
1135 else if ( const QgsSingleBandPseudoColorRenderer *pseudoColorRenderer = dynamic_cast<const QgsSingleBandPseudoColorRenderer *>( renderer ) )
1136 {
1137 if ( const QgsRasterShader *shader = pseudoColorRenderer->shader() )
1138 {
1139 if ( const QgsColorRampShader *shaderFunction = dynamic_cast<const QgsColorRampShader *>( shader->rasterShaderFunction() ) )
1140 {
1142 switch ( shaderFunction->colorRampType() )
1143 {
1145 {
1146 rat->appendField( u"Min"_s, Qgis::RasterAttributeTableFieldUsage::Min, QMetaType::Type::Double );
1147 rat->appendField( u"Max"_s, Qgis::RasterAttributeTableFieldUsage::Max, QMetaType::Type::Double );
1148 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1149 rat->appendField( u"RedMin"_s, Qgis::RasterAttributeTableFieldUsage::RedMin, QMetaType::Type::Int );
1150 rat->appendField( u"GreenMin"_s, Qgis::RasterAttributeTableFieldUsage::GreenMin, QMetaType::Type::Int );
1151 rat->appendField( u"BlueMin"_s, Qgis::RasterAttributeTableFieldUsage::BlueMin, QMetaType::Type::Int );
1152 rat->appendField( u"AlphaMin"_s, Qgis::RasterAttributeTableFieldUsage::AlphaMin, QMetaType::Type::Int );
1153 rat->appendField( u"RedMax"_s, Qgis::RasterAttributeTableFieldUsage::RedMax, QMetaType::Type::Int );
1154 rat->appendField( u"GreenMax"_s, Qgis::RasterAttributeTableFieldUsage::GreenMax, QMetaType::Type::Int );
1155 rat->appendField( u"BlueMax"_s, Qgis::RasterAttributeTableFieldUsage::BlueMax, QMetaType::Type::Int );
1156 rat->appendField( u"AlphaMax"_s, Qgis::RasterAttributeTableFieldUsage::AlphaMax, QMetaType::Type::Int );
1157 const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
1158 if ( rampItems.size() > 1 )
1159 {
1160 QColor color1 { rampItems.at( 0 ).color };
1161 QString label1 { rampItems.at( 0 ).label };
1162 QVariant value1( rampItems.at( 0 ).value );
1163 const int rampItemSize = static_cast<int>( rampItems.size() );
1164 for ( int i = 1; i < rampItemSize; ++i )
1165 {
1166 const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i ) };
1167 rat->appendRow( QVariantList() << value1 << rampItem.value << u"%1 - %2"_s.arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
1168 rat->setRamp( static_cast<int>( rat->data().length() - 1 ), color1, rampItem.color );
1169 label1 = rampItem.label;
1170 value1 = rampItem.value;
1171 color1 = rampItem.color;
1172 }
1173 }
1174 break;
1175 }
1176
1178 {
1179 rat->appendField( u"Min"_s, Qgis::RasterAttributeTableFieldUsage::Min, QMetaType::Type::Double );
1180 rat->appendField( u"Max"_s, Qgis::RasterAttributeTableFieldUsage::Max, QMetaType::Type::Double );
1181 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1182 rat->appendField( u"Red"_s, Qgis::RasterAttributeTableFieldUsage::Red, QMetaType::Type::Int );
1183 rat->appendField( u"Green"_s, Qgis::RasterAttributeTableFieldUsage::Green, QMetaType::Type::Int );
1184 rat->appendField( u"Blue"_s, Qgis::RasterAttributeTableFieldUsage::Blue, QMetaType::Type::Int );
1185 rat->appendField( u"Alpha"_s, Qgis::RasterAttributeTableFieldUsage::Alpha, QMetaType::Type::Int );
1186 const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
1187 if ( rampItems.size() > 1 )
1188 {
1189 QColor color1 { rampItems.at( 0 ).color };
1190 QString label1 { rampItems.at( 0 ).label };
1191 QVariant value1( rampItems.at( 0 ).value );
1192 const int rampItemSize = static_cast<int>( rampItems.size() );
1193 for ( int i = 1; i < rampItemSize; ++i )
1194 {
1195 const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i ) };
1196 rat->appendRow( QVariantList() << value1 << rampItem.value << u"%1 - %2"_s.arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
1197 rat->setRamp( static_cast<int>( rat->data().length() - 1 ), color1, rampItem.color );
1198 label1 = rampItem.label;
1199 value1 = rampItem.value;
1200 color1 = rampItem.color;
1201 }
1202 }
1203 break;
1204 }
1205
1207 {
1208 rat->appendField( u"Value"_s, Qgis::RasterAttributeTableFieldUsage::MinMax, QMetaType::Type::Double );
1209 rat->appendField( u"Class"_s, Qgis::RasterAttributeTableFieldUsage::Name, QMetaType::Type::QString );
1210 rat->appendField( u"Red"_s, Qgis::RasterAttributeTableFieldUsage::Red, QMetaType::Type::Int );
1211 rat->appendField( u"Green"_s, Qgis::RasterAttributeTableFieldUsage::Green, QMetaType::Type::Int );
1212 rat->appendField( u"Blue"_s, Qgis::RasterAttributeTableFieldUsage::Blue, QMetaType::Type::Int );
1213 rat->appendField( u"Alpha"_s, Qgis::RasterAttributeTableFieldUsage::Alpha, QMetaType::Type::Int );
1214 const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
1215 for ( const QgsColorRampShader::ColorRampItem &rampItem : std::as_const( rampItems ) )
1216 {
1217 rat->appendRow( QVariantList() << rampItem.value << rampItem.label << 0 << 0 << 0 << 255 );
1218 rat->setColor( static_cast<int>( rat->data().length() - 1 ), rampItem.color );
1219 }
1220 break;
1221 }
1222 }
1223
1224 if ( bandNumber )
1225 {
1226 *bandNumber = pseudoColorRenderer->inputBand();
1227 }
1228
1229 return rat;
1230 }
1231 else
1232 {
1233 return nullptr;
1234 }
1235 }
1236 else
1237 {
1238 return nullptr;
1239 }
1240 }
1241 else
1242 {
1243 return nullptr;
1244 }
1245}
1246
1247QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> QgsRasterAttributeTable::usageInformation()
1248{
1249 std::call_once( usageInformationLoaderFlag, [] {
1250 QgsRasterAttributeTable::sUsageInformation.insert(
1252 { 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 }
1253 );
1254 QgsRasterAttributeTable::sUsageInformation
1255 .insert( Qgis::RasterAttributeTableFieldUsage::PixelCount, { tr( "Histogram Pixel Count" ), true, false, false, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::LongLong } );
1256 QgsRasterAttributeTable::sUsageInformation
1257 .insert( Qgis::RasterAttributeTableFieldUsage::Name, { tr( "Class Name" ), false, false, false, false, true, true, QList<QMetaType::Type>() << QMetaType::Type::QString } );
1258 QgsRasterAttributeTable::sUsageInformation
1259 .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 } );
1260 QgsRasterAttributeTable::sUsageInformation
1261 .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 } );
1262 QgsRasterAttributeTable::sUsageInformation
1263 .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 } );
1264 QgsRasterAttributeTable::sUsageInformation
1265 .insert( Qgis::RasterAttributeTableFieldUsage::Red, { tr( "Red Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1266 QgsRasterAttributeTable::sUsageInformation
1267 .insert( Qgis::RasterAttributeTableFieldUsage::Green, { tr( "Green Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1268 QgsRasterAttributeTable::sUsageInformation
1269 .insert( Qgis::RasterAttributeTableFieldUsage::Blue, { tr( "Blue Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1270 QgsRasterAttributeTable::sUsageInformation
1271 .insert( Qgis::RasterAttributeTableFieldUsage::Alpha, { tr( "Alpha Color Value (0-255)" ), true, false, true, false, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1272 QgsRasterAttributeTable::sUsageInformation
1273 .insert( Qgis::RasterAttributeTableFieldUsage::RedMin, { tr( "Red Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1274 QgsRasterAttributeTable::sUsageInformation
1275 .insert( Qgis::RasterAttributeTableFieldUsage::GreenMin, { tr( "Green Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1276 QgsRasterAttributeTable::sUsageInformation
1277 .insert( Qgis::RasterAttributeTableFieldUsage::BlueMin, { tr( "Blue Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1278 QgsRasterAttributeTable::sUsageInformation
1279 .insert( Qgis::RasterAttributeTableFieldUsage::AlphaMin, { tr( "Alpha Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1280 QgsRasterAttributeTable::sUsageInformation
1281 .insert( Qgis::RasterAttributeTableFieldUsage::RedMax, { tr( "Red Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1282 QgsRasterAttributeTable::sUsageInformation
1283 .insert( Qgis::RasterAttributeTableFieldUsage::GreenMax, { tr( "Green Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1284 QgsRasterAttributeTable::sUsageInformation
1285 .insert( Qgis::RasterAttributeTableFieldUsage::BlueMax, { tr( "Blue Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1286 QgsRasterAttributeTable::sUsageInformation
1287 .insert( Qgis::RasterAttributeTableFieldUsage::AlphaMax, { tr( "Alpha Color Minimum Value (0-255)" ), true, false, false, true, true, false, QList<QMetaType::Type>() << QMetaType::Type::Int } );
1288 // Unsupported!!
1289 QgsRasterAttributeTable::sUsageInformation
1290 .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 } );
1291 } );
1292 return QgsRasterAttributeTable::sUsageInformation;
1293}
1294
1295void QgsRasterAttributeTable::setType()
1296{
1297 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1299}
1300
1301
1303QHash<int, QgsRasterAttributeTable::UsageInformation> QgsRasterAttributeTable::usageInformationInt()
1304{
1305 QHash<int, QgsRasterAttributeTable::UsageInformation> usageInfoInt;
1306 const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
1307 for ( auto it = usageInfo.cbegin(); it != usageInfo.cend(); ++it )
1308 {
1309 usageInfoInt.insert( static_cast<int>( it.key() ), it.value() );
1310 }
1311 return usageInfoInt;
1312}
1314
1316{
1317 return mFilePath;
1318}
1319
1320QList<QgsRasterAttributeTable::MinMaxClass> QgsRasterAttributeTable::minMaxClasses( const int classificationColumn ) const
1321{
1322 QList<QgsRasterAttributeTable::MinMaxClass> classes;
1323 if ( !isValid() )
1324 {
1325 QgsDebugError( "minMaxClasses was called on an invalid RAT" );
1326 return classes;
1327 }
1328
1329 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1330
1331 if ( !fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
1332 {
1333 QgsDebugError( "minMaxClasses was called on a ramp raster" );
1334 return classes;
1335 }
1336
1337 const int minMaxIndex { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) ) };
1338
1339 Q_ASSERT( minMaxIndex >= 0 );
1340
1341 int classificationIndex = classificationColumn;
1342 if ( classificationIndex >= 0 && classificationIndex < mFields.count() )
1343 {
1344 const Field classificationField { mFields.at( classificationIndex ) };
1345 if ( ( classificationField.usage != Qgis::RasterAttributeTableFieldUsage::Name && classificationField.usage != Qgis::RasterAttributeTableFieldUsage::Generic ) )
1346 {
1347 QgsDebugError( "minMaxClasses was called with a classification column which is not suitable for classification" );
1348 return classes;
1349 }
1350 }
1351 else if ( classificationIndex == -1 ) // Special value for not-set
1352 {
1353 // Find first value or generic field
1354 if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Name ) )
1355 {
1356 classificationIndex = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Name ) );
1357 }
1358 else if ( fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::Generic ) )
1359 {
1360 classificationIndex = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Generic ) );
1361 }
1362 else
1363 {
1364 classificationIndex = minMaxIndex;
1365 }
1366 }
1367 else if ( classificationIndex >= mFields.count() )
1368 {
1369 QgsDebugError( "minMaxClasses was called with a classification column out of range" );
1370 return classes;
1371 }
1372
1373 if ( classificationIndex >= 0 )
1374 {
1375 QStringList labels;
1376 int rowIdx { 0 };
1377 for ( const QVariantList &row : std::as_const( mData ) )
1378 {
1379 const QString label { row.at( classificationIndex ).toString() };
1380 bool ok;
1381 const double value { row.at( minMaxIndex ).toDouble( &ok ) };
1382 // This should never happen, could eventually become a Q_ASSERT
1383 if ( !ok )
1384 {
1385 QgsDebugError( "minMaxClasses could not convert a MinMax value to double" );
1386 return classes;
1387 }
1388 if ( labels.contains( label ) )
1389 {
1390 classes[labels.indexOf( label )].minMaxValues.push_back( value );
1391 }
1392 else
1393 {
1394 labels.push_back( label );
1395 classes.push_back( { label, { value }, color( rowIdx ) } );
1396 }
1397 rowIdx++;
1398 }
1399 }
1400 return classes;
1401}
1402
1403QgsGradientColorRamp QgsRasterAttributeTable::colorRamp( QStringList &labels, const int labelColumn ) const
1404{
1405 QgsGradientColorRamp ramp { Qt::GlobalColor::white, Qt::GlobalColor::black };
1406 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1407 const int minIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) ) };
1408 const int maxIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) ) };
1409 const bool isRange { minIdx >= 0 && maxIdx >= 0 };
1410
1411 int labelIdx { labelColumn };
1412 if ( labelColumn < 0
1413 || labelColumn >= fields().count()
1414 || ( fieldUsages.at( labelColumn ) != Qgis::RasterAttributeTableFieldUsage::Name && fieldUsages.at( labelColumn ) != Qgis::RasterAttributeTableFieldUsage::Generic ) )
1415 {
1416 labelIdx = -1;
1417 }
1418
1419 if ( !mData.isEmpty() && ( minIdx >= 0 && maxIdx >= 0 ) )
1420 {
1422 const bool hasColorOrRamp { hasColor() || hasRamp() };
1423 {
1424 const double min { minimumValue() };
1425 const double max { maximumValue() };
1426 const double range { max - min };
1427
1428 if ( range != 0 )
1429 {
1430 if ( !std::isnan( min ) && !std::isnan( max ) )
1431 {
1432 const QList<QVariantList> dataCopy( orderedRows() );
1433
1434 QgsRasterAttributeTable orderedRat;
1435 for ( const Field &f : std::as_const( mFields ) )
1436 {
1437 orderedRat.appendField( f );
1438 }
1439 for ( const QVariantList &r : std::as_const( dataCopy ) )
1440 {
1441 orderedRat.appendRow( r );
1442 }
1443
1444 QColor lastColor { ramp.color1() };
1445
1446 if ( hasColorOrRamp )
1447 {
1448 ramp.setColor1( orderedRat.hasColor() ? orderedRat.color( 0 ) : orderedRat.ramp( 0 ).color1() );
1449 ramp.setColor2( orderedRat.hasColor() ? orderedRat.color( static_cast<int>( orderedRat.data().count() - 1 ) ) : orderedRat.ramp( static_cast<int>( orderedRat.data().count() - 1 ) ).color2() );
1450 lastColor = orderedRat.hasColor() ? orderedRat.color( 0 ) : orderedRat.ramp( 0 ).color2();
1451 }
1452
1453 auto labelFromField = [&]( int rowIdx ) -> QString {
1454 if ( labelIdx < 0 )
1455 {
1456 return u"%L1 - %L2"_s.arg( orderedRat.value( rowIdx, minIdx ).toDouble() ).arg( orderedRat.value( rowIdx, maxIdx ).toDouble() );
1457 }
1458 const QVariant val( orderedRat.value( rowIdx, labelIdx ) );
1459 bool ok { true };
1460 QString res;
1461 switch ( val.userType() )
1462 {
1463 case QMetaType::Type::QChar:
1464 return QString( val.toChar() );
1465 case QMetaType::Type::Int:
1466 res = QLocale().toString( val.toInt( &ok ) );
1467 break;
1468 case QMetaType::Type::LongLong:
1469 res = QLocale().toString( val.toLongLong( &ok ) );
1470 break;
1471 case QMetaType::Type::UInt:
1472 res = QLocale().toString( val.toUInt( &ok ) );
1473 break;
1474 case QMetaType::Type::ULongLong:
1475 res = QLocale().toString( val.toULongLong( &ok ) );
1476 break;
1477 case QMetaType::Type::Double:
1478 res = QLocale().toString( val.toDouble( &ok ), 'g' );
1479 break;
1480 case QMetaType::Type::QString:
1481 default:
1482 return val.toString();
1483 }
1484 return ok ? res : val.toString();
1485 };
1486
1487 // Case 1: range classes, discrete colors
1488 // - create stops for the lower value of each class except for the first,
1489 // - use the color from the previous class
1490 if ( orderedRat.hasColor() && isRange )
1491 {
1492 labels.push_back( labelFromField( 0 ) );
1493
1494 for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
1495 {
1496 const double offset { ( orderedRat.value( rowIdx, minIdx ).toDouble() - min ) / range };
1497 const QColor color { orderedRat.color( rowIdx - 1 ) };
1498 stops.append( QgsGradientStop( offset, color ) );
1499 labels.push_back( labelFromField( rowIdx ) );
1500 }
1501 }
1502 // Case 2: range classes, gradients colors
1503 // Take the class bounds (average value between max of previous class and min of the next)
1504 // to avoid potential overlapping or gaps between classes.
1505 // Create stop:
1506 // - first stop at value taking the max color of the previous class
1507 // - second stop at value + epsilon taking the min color of the next class, unless colors and offset are equal
1508 else if ( orderedRat.hasRamp() && isRange )
1509 {
1510 double prevOffset { 0 };
1511 labels.push_back( labelFromField( 0 ) );
1512 for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
1513 {
1514 labels.push_back( labelFromField( rowIdx ) );
1515 const int prevRowIdx { rowIdx - 1 };
1516 const double offset { ( ( orderedRat.value( rowIdx, minIdx ).toDouble() + orderedRat.value( prevRowIdx, maxIdx ).toDouble() ) / 2.0 - min ) / range };
1517 const QgsGradientColorRamp previousRamp { orderedRat.ramp( prevRowIdx ) };
1518 stops.append( QgsGradientStop( offset, previousRamp.color2() ) );
1519
1520 const QgsGradientColorRamp currentRamp { orderedRat.ramp( rowIdx ) };
1521 // An additional stop is added if the colors are different or offsets are different by 1e-6 (offset varies from 0 to 1).
1522 if ( currentRamp.color1() != previousRamp.color2() && qgsDoubleNear( offset, prevOffset, 1e-6 ) )
1523 {
1524 stops.append( QgsGradientStop( offset + std::numeric_limits<double>::epsilon(), currentRamp.color1() ) );
1525 }
1526 prevOffset = offset;
1527 }
1528 }
1529 // Case 3: range classes but no colors at all
1530 // Take the class borders (average value between max of previous class and min of the next)
1531 // Create stop for the lower class, actually skipping the upper bound of the last class
1532 else
1533 {
1534 labels.push_back( labelFromField( 0 ) );
1535
1536 for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
1537 {
1538 const int prevRowIdx { rowIdx - 1 };
1539 const double offset { ( ( orderedRat.value( rowIdx, minIdx ).toDouble() + orderedRat.value( prevRowIdx, maxIdx ).toDouble() ) / 2.0 - min ) / range };
1540 stops.append( QgsGradientStop( offset, ramp.color( offset ) ) );
1541 labels.push_back( labelFromField( rowIdx ) );
1542 }
1543 }
1544 }
1545 }
1546 }
1547
1548 ramp.setStops( stops );
1549 ramp.setDiscrete( hasColor() );
1550 }
1551
1552 return ramp;
1553}
1554
1555QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvider *provider, const int bandNumber, const int classificationColumn )
1556{
1557 if ( !provider )
1558 {
1559 return nullptr;
1560 }
1561
1562 std::unique_ptr<QgsRasterRenderer> renderer;
1563
1565 {
1566 std::unique_ptr<QgsColorRamp> ramp;
1567 if ( !hasColor() )
1568 {
1569 ramp = std::make_unique<QgsRandomColorRamp>();
1570 }
1572 if ( classes.isEmpty() )
1573 return nullptr;
1574
1575 renderer = std::make_unique<QgsPalettedRasterRenderer>( provider, bandNumber, classes );
1576 }
1577 else
1578 {
1579 auto pseudoColorRenderer = std::make_unique<QgsSingleBandPseudoColorRenderer>( provider, bandNumber );
1580 QStringList labels;
1581 // Athematic classification is not supported, but the classificationColumn will be used for labels.
1582 // if it's not specified, try to guess it here.
1583 int labelColumn { classificationColumn };
1584 if ( labelColumn < 0 )
1585 {
1586 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1587 labelColumn = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Name ) );
1588 if ( labelColumn < 0 )
1589 {
1590 labelColumn = static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Generic ) );
1591 }
1592 }
1593 QgsGradientColorRamp *ramp { colorRamp( labels, labelColumn ).clone() };
1594 pseudoColorRenderer->setClassificationMin( minimumValue() );
1595 pseudoColorRenderer->setClassificationMax( maximumValue() );
1596 // Use discrete for single colors, interpolated for ramps
1597 pseudoColorRenderer->createShader(
1599 );
1600 if ( pseudoColorRenderer->shader() )
1601 {
1602 pseudoColorRenderer->shader()->setMaximumValue( maximumValue() );
1603 pseudoColorRenderer->shader()->setMinimumValue( minimumValue() );
1604 // Set labels
1605 if ( QgsColorRampShader *shaderFunction = static_cast<QgsColorRampShader *>( pseudoColorRenderer->shader()->rasterShaderFunction() ) )
1606 {
1607 shaderFunction->setMinimumValue( minimumValue() );
1608 shaderFunction->setMaximumValue( maximumValue() );
1609 const bool labelsAreUsable { ramp->count() > 2 && labels.count() == ramp->count() - 1 };
1610
1611 if ( labelsAreUsable )
1612 {
1613 QList<QgsColorRampShader::ColorRampItem> newItemList;
1614 const double range { maximumValue() - minimumValue() };
1615 int stopIdx { 0 };
1616 for ( const QString &label : std::as_const( labels ) )
1617 {
1618 if ( stopIdx >= ramp->count() - 2 )
1619 {
1620 break;
1621 }
1622 double value { minimumValue() + ramp->stops().at( stopIdx ).offset * range };
1623 QgsColorRampShader::ColorRampItem item { value, ramp->stops().at( stopIdx ).color, label };
1624 newItemList.push_back( item );
1625 stopIdx++;
1626 }
1627
1628 QgsColorRampShader::ColorRampItem item { maximumValue(), ramp->color2(), labels.last() };
1629 newItemList.push_back( item );
1630 shaderFunction->setColorRampItemList( newItemList );
1631 }
1632 }
1633 }
1634 renderer = std::move( pseudoColorRenderer );
1635 }
1636
1637 return renderer.release();
1638}
1639
1640QList<QList<QVariant> > QgsRasterAttributeTable::orderedRows() const
1641{
1642 const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
1643 const int minIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) ) };
1644 const int maxIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) ) };
1645 const bool isRange { minIdx >= 0 && maxIdx >= 0 };
1646 QList<QVariantList> dataCopy( mData );
1647
1648 if ( isRange )
1649 {
1650 std::sort( dataCopy.begin(), dataCopy.end(), [&]( const QVariantList &first, const QVariantList &second ) -> bool {
1651 return ( first.at( maxIdx ).toDouble() + first.at( minIdx ).toDouble() ) < ( second.at( maxIdx ).toDouble() + second.at( minIdx ).toDouble() );
1652 } );
1653 }
1654 else
1655 {
1656 const int minMaxIdx { static_cast<int>( fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) ) };
1657 if ( minMaxIdx < 0 )
1658 {
1659 return dataCopy;
1660 }
1661 else
1662 {
1663 std::sort( dataCopy.begin(), dataCopy.end(), [&]( const QVariantList &first, const QVariantList &second ) -> bool { return first.at( minMaxIdx ).toDouble() < second.at( minMaxIdx ).toDouble(); } );
1664 }
1665 }
1666
1667 return dataCopy;
1668}
1669
1677
@ Exact
Assigns the color of the exact matching value in the color ramp item list.
Definition qgis.h:1506
@ Linear
Interpolates the color between two class breaks linearly.
Definition qgis.h:1504
@ Discrete
Assigns the color of the higher class for every pixel between two class breaks.
Definition qgis.h:1505
RasterAttributeTableType
The RasterAttributeTableType enum represents the type of RAT.
Definition qgis.h:1695
@ Continuous
Uses breaks from color palette.
Definition qgis.h:1519
RasterAttributeTableFieldUsage
The RasterAttributeTableFieldUsage enum represents the usage of a Raster Attribute Table field.
Definition qgis.h:1666
@ RedMax
Field usage RedMax.
Definition qgis.h:1681
@ PixelCount
Field usage PixelCount.
Definition qgis.h:1668
@ AlphaMax
Field usage AlphaMax.
Definition qgis.h:1684
@ BlueMin
Field usage BlueMin.
Definition qgis.h:1679
@ Alpha
Field usage Alpha.
Definition qgis.h:1676
@ Generic
Field usage Generic.
Definition qgis.h:1667
@ GreenMax
Field usage GreenMax.
Definition qgis.h:1682
@ RedMin
Field usage RedMin.
Definition qgis.h:1677
@ MinMax
Field usage MinMax.
Definition qgis.h:1672
@ BlueMax
Field usage BlueMax.
Definition qgis.h:1683
@ AlphaMin
Field usage AlphaMin.
Definition qgis.h:1680
@ Green
Field usage Green.
Definition qgis.h:1674
@ MaxCount
Not used by QGIS: GDAL Maximum GFU value (equals to GFU_AlphaMax+1 currently).
Definition qgis.h:1685
@ GreenMin
Field usage GreenMin.
Definition qgis.h:1678
@ 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:69
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:6975
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
QList< QgsFeature > QgsFeatureList
#define QgsDebugError(str)
Definition qgslogger.h:59
Setting options for creating vector data providers.
Properties of a single value class.