mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-15 00:07:25 -05:00
Handle other rat types
This commit is contained in:
parent
62dff87ccf
commit
3479a66e4e
@ -70,11 +70,6 @@ Returns ``True`` if the field carries a color ramp component information (RedMin
|
||||
Qgis::RasterAttributeTableType type() const;
|
||||
%Docstring
|
||||
Returns the Raster Attribute Table type.
|
||||
%End
|
||||
|
||||
void setType( const Qgis::RasterAttributeTableType type );
|
||||
%Docstring
|
||||
Sets the Raster Attribute Table ``type``
|
||||
%End
|
||||
|
||||
bool hasColor() const;
|
||||
@ -223,6 +218,13 @@ Creates a new field from ``name``, ``usage`` and ``type`` and inserts it at ``po
|
||||
bool insertColor( int position, QString *errorMessage /Out/ = 0 );
|
||||
%Docstring
|
||||
Create RGBA fields and inserts them at ``position``, optionally reporting any error in ``errorMessage``, returns ``True`` on success.
|
||||
%End
|
||||
|
||||
bool setFieldUsage( int fieldIndex, const Qgis::RasterAttributeTableFieldUsage usage );
|
||||
%Docstring
|
||||
Change the usage of the field at index ``fieldIndex`` to ``usage`` with checks for allowed types.
|
||||
|
||||
:return: ``True`` on success.
|
||||
%End
|
||||
|
||||
bool insertRamp( int position, QString *errorMessage /Out/ = 0 );
|
||||
@ -336,15 +338,30 @@ the classification column based on the field usage.
|
||||
QgsGradientColorRamp colorRamp( QStringList &labels /Out/, const int labelColumn = -1 ) const;
|
||||
%Docstring
|
||||
Returns the color ramp for an athematic Raster Attribute Table
|
||||
returning the ``labels``, optionally generated from ``labelColumn``.
|
||||
setting the labels in ``labels``, optionally generated from ``labelColumn``.
|
||||
%End
|
||||
|
||||
QgsRasterRenderer *createRenderer( QgsRasterDataProvider *provider, const int bandNumber, const int classificationColumn = -1 ) /Factory/;
|
||||
%Docstring
|
||||
Creates and returns a (possibly ``None``) raster renderer for the
|
||||
specified ``provider`` and ``bandNumber`` and optionally classified
|
||||
specified ``provider`` and ``bandNumber`` and optionally reclassified
|
||||
by ``classificationColumn``, the default value of -1 makes the method
|
||||
guess the classification column based on the field usage.
|
||||
|
||||
.. note::
|
||||
|
||||
athematic attribute tables with color ramps cannot be reclassified,
|
||||
the renderer will still use the ``classificationColumn`` for
|
||||
generating the class labels.
|
||||
%End
|
||||
|
||||
QList<QList<QVariant>> orderedRows( ) const;
|
||||
%Docstring
|
||||
Returns the data rows ordered by the value column(s) in ascending order, if
|
||||
the attribute table type is athematic the middle value for each row range
|
||||
is considered for ordering.
|
||||
If the attribute table does not have any value field (and hence is not valid),
|
||||
the current data are returned without any change.
|
||||
%End
|
||||
|
||||
static Qgis::RasterAttributeTableFieldUsage guessFieldUsage( const QString &name, const QVariant::Type type );
|
||||
@ -389,7 +406,6 @@ Returns information about supported Raster Attribute Table usages.
|
||||
.. seealso:: :py:func:`usageName`
|
||||
%End
|
||||
|
||||
static QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> sUsageInformation;
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -748,7 +748,7 @@ Loads the filesystem-based attribute table for the specified ``bandNumber`` from
|
||||
.. versionadded:: 3.30
|
||||
%End
|
||||
|
||||
virtual bool writeNativeAttributeTable( QString *errorMessage /Out/ = 0 ) const; //#spellok
|
||||
virtual bool writeNativeAttributeTable( QString *errorMessage /Out/ = 0 ); //#spellok
|
||||
|
||||
virtual bool readNativeAttributeTable( QString *errorMessage /Out/ = 0 );
|
||||
%Docstring
|
||||
|
||||
@ -12061,10 +12061,11 @@ void QgisApp::createRasterAttributeTable()
|
||||
layer->dataProvider()->setAttributeTable( bandNumber, rat );
|
||||
|
||||
// Save it
|
||||
const QString filePath { dlg.filePath() };
|
||||
if ( ! filePath.isEmpty() )
|
||||
const bool saveToFile { dlg.saveToFile() };
|
||||
if ( saveToFile )
|
||||
{
|
||||
if ( QMessageBox::question( nullptr, tr( "Confirm Overwrite" ), tr( "Are you sure you want to overwrite the existing attribute table at '%1'?" ).arg( filePath ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
|
||||
const QString filePath { dlg.filePath() };
|
||||
if ( QFile::exists( filePath ) && QMessageBox::question( nullptr, tr( "Confirm Overwrite" ), tr( "Are you sure you want to overwrite the existing attribute table at '%1'?" ).arg( filePath ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3114,63 +3114,6 @@ bool QgsGdalProvider::readNativeAttributeTable( QString *errorMessage )
|
||||
usages.append( usage );
|
||||
}
|
||||
|
||||
QStringList ratFieldNames { ratFields.names( ) };
|
||||
QStringList lNames;
|
||||
|
||||
// Try to identify fields in case of Raster Attribute Table with wrong usages
|
||||
if ( ! usages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) &&
|
||||
!( usages.contains( Qgis::RasterAttributeTableFieldUsage::Min ) && usages.contains( Qgis::RasterAttributeTableFieldUsage::Max ) ) )
|
||||
{
|
||||
if ( lowerNames.contains( QStringLiteral( "value" ) ) )
|
||||
{
|
||||
usages[ lowerNames.indexOf( QLatin1String( "value" ) ) ] = Qgis::RasterAttributeTableFieldUsage::MinMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
const QStringList minValueNames { {
|
||||
QStringLiteral( "min" ),
|
||||
QStringLiteral( "min_value" ),
|
||||
QStringLiteral( "min value" ),
|
||||
QStringLiteral( "value min" ),
|
||||
QStringLiteral( "value_min" ),
|
||||
} };
|
||||
|
||||
for ( const QString &minName : std::as_const( minValueNames ) )
|
||||
{
|
||||
if ( lowerNames.contains( minName ) )
|
||||
{
|
||||
usages[ lowerNames.indexOf( minName ) ] = Qgis::RasterAttributeTableFieldUsage::Min;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const QStringList maxValueNames { {
|
||||
QStringLiteral( "max" ),
|
||||
QStringLiteral( "max_value" ),
|
||||
QStringLiteral( "max value" ),
|
||||
QStringLiteral( "value max" ),
|
||||
QStringLiteral( "value_max" ),
|
||||
} };
|
||||
|
||||
for ( const QString &maxName : std::as_const( minValueNames ) )
|
||||
{
|
||||
if ( lowerNames.contains( maxName ) )
|
||||
{
|
||||
usages[ lowerNames.indexOf( maxName ) ] = Qgis::RasterAttributeTableFieldUsage::Max;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! usages.contains( Qgis::RasterAttributeTableFieldUsage::PixelCount ) )
|
||||
{
|
||||
if ( lowerNames.contains( QStringLiteral( "count" ) ) )
|
||||
{
|
||||
usages[ lowerNames.indexOf( QLatin1String( "count" ) ) ] = Qgis::RasterAttributeTableFieldUsage::PixelCount;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsRasterAttributeTable> rat = std::make_unique<QgsRasterAttributeTable>();
|
||||
|
||||
for ( const auto &field : std::as_const( ratFields ) )
|
||||
@ -3208,33 +3151,58 @@ bool QgsGdalProvider::readNativeAttributeTable( QString *errorMessage )
|
||||
rat->appendRow( rowData );
|
||||
}
|
||||
|
||||
// Try to cope with invalid rats due to generic fields
|
||||
if ( ! rat->isValid( ) )
|
||||
{
|
||||
std::unique_ptr<QgsRasterAttributeTable> ratCopy = std::make_unique<QgsRasterAttributeTable>( *rat );
|
||||
bool changed { false };
|
||||
for ( int fieldIdx = 0; fieldIdx < ratCopy->fields().count( ); ++fieldIdx )
|
||||
{
|
||||
const QgsRasterAttributeTable::Field field { ratCopy->fields().at( fieldIdx ) };
|
||||
if ( field.usage == Qgis::RasterAttributeTableFieldUsage::Generic )
|
||||
{
|
||||
const Qgis::RasterAttributeTableFieldUsage newUsage { QgsRasterAttributeTable::guessFieldUsage( field.name, field.type ) };
|
||||
if ( newUsage != Qgis::RasterAttributeTableFieldUsage::Generic && ratCopy->setFieldUsage( fieldIdx, newUsage ) )
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Did that work?
|
||||
if ( changed && ratCopy->isValid( ) )
|
||||
{
|
||||
rat.reset( ratCopy.release() );
|
||||
}
|
||||
}
|
||||
|
||||
hasAtLeastOnedRat = rat->fields().count( ) > 0;
|
||||
|
||||
if ( hasAtLeastOnedRat )
|
||||
{
|
||||
rat->setType( static_cast<Qgis::RasterAttributeTableType>( GDALRATGetTableType( hRat ) ) );
|
||||
rat->setDirty( false );
|
||||
setAttributeTable( bandNumber, rat.release() );
|
||||
}
|
||||
else if ( errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "Raster Attribute Table has no columns: skipping." );
|
||||
*errorMessage = QObject::tr( "Raster attribute table has no columns: skipping." );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "Dataset is not valid and Raster Attribute Table could not be loaded." );
|
||||
*errorMessage = QObject::tr( "Dataset is not valid and raster attribute table could not be loaded." );
|
||||
}
|
||||
|
||||
return hasAtLeastOnedRat;
|
||||
}
|
||||
|
||||
|
||||
bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) const //#spellok
|
||||
bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) //#spellok
|
||||
{
|
||||
bool success { false };
|
||||
bool wasReopenedReadWrite { false };
|
||||
for ( int band = 1; band <= bandCount(); band++ )
|
||||
{
|
||||
QgsRasterAttributeTable *rat { attributeTable( band ) };
|
||||
@ -3242,101 +3210,136 @@ bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) const /
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ( rat->isDirty() )
|
||||
|
||||
// Needs to be in write mode for HFA and perhaps other formats!
|
||||
if ( GDALGetAccess( mGdalDataset ) == GA_ReadOnly )
|
||||
{
|
||||
GDALRasterBandH hBand { GDALGetRasterBand( mGdalBaseDataset, band ) };
|
||||
GDALRasterAttributeTableH hRat = GDALCreateRasterAttributeTable( );
|
||||
if ( GDALRATSetTableType( hRat, static_cast<GDALRATTableType>( rat->type() ) ) != CE_None )
|
||||
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read/write mode" ) );
|
||||
GDALClose( mGdalDataset );
|
||||
|
||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_UPDATE );
|
||||
|
||||
// if the dataset couldn't be opened in read / write mode, tell the user
|
||||
if ( !mGdalBaseDataset )
|
||||
{
|
||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_READONLY );
|
||||
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
||||
mGdalDataset = mGdalBaseDataset;
|
||||
if ( errorMessage )
|
||||
{
|
||||
*errorMessage = tr( "GDAL Error reopening dataset in write mode, raster attribute table could not be saved." );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
wasReopenedReadWrite = true;
|
||||
}
|
||||
|
||||
GDALRasterBandH hBand { GDALGetRasterBand( mGdalBaseDataset, band ) };
|
||||
GDALRasterAttributeTableH hRat = GDALCreateRasterAttributeTable( );
|
||||
if ( GDALRATSetTableType( hRat, static_cast<GDALRATTableType>( rat->type() ) ) != CE_None )
|
||||
{
|
||||
if ( errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "GDAL error setting the table type, raster attribute table could not be saved." );
|
||||
}
|
||||
GDALDestroyRasterAttributeTable( hRat );
|
||||
return false;
|
||||
}
|
||||
const QList<QgsRasterAttributeTable::Field> ratFields { rat->fields() };
|
||||
QMap<int, GDALRATFieldType> typeMap;
|
||||
|
||||
int colIdx { 0 };
|
||||
for ( const QgsRasterAttributeTable::Field &field : std::as_const( ratFields ) )
|
||||
{
|
||||
GDALRATFieldType fType { GFT_String };
|
||||
switch ( field.type )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
{
|
||||
fType = GFT_Integer;
|
||||
break;
|
||||
}
|
||||
case QVariant::Double:
|
||||
{
|
||||
fType = GFT_Real;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fType = GFT_String;
|
||||
}
|
||||
if ( GDALRATCreateColumn( hRat, field.name.toStdString().c_str(), fType, static_cast<GDALRATFieldUsage>( field.usage ) ) != CE_None )
|
||||
{
|
||||
if ( errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "RAT table type could not be set, Raster Attribute Table was not saved (GDAL error)." );
|
||||
*errorMessage = QObject::tr( "GDAL error creating column '%1, raster attribute table could not be saved." ).arg( field.name );
|
||||
}
|
||||
GDALDestroyRasterAttributeTable( hRat );
|
||||
return false;
|
||||
}
|
||||
const QList<QgsRasterAttributeTable::Field> ratFields { rat->fields() };
|
||||
QMap<int, GDALRATFieldType> typeMap;
|
||||
typeMap[ colIdx ] = fType;
|
||||
colIdx++;
|
||||
}
|
||||
|
||||
int colIdx { 0 };
|
||||
for ( const QgsRasterAttributeTable::Field &field : std::as_const( ratFields ) )
|
||||
// Save data
|
||||
const QList<QVariantList> data { rat->data() };
|
||||
int rowIdx { 0 };
|
||||
for ( const auto &row : std::as_const( data ) )
|
||||
{
|
||||
for ( int colIdx = 0; colIdx < row.size(); colIdx++ )
|
||||
{
|
||||
GDALRATFieldType fType { GFT_String };
|
||||
switch ( field.type )
|
||||
switch ( typeMap[ colIdx ] )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
{
|
||||
fType = GFT_Integer;
|
||||
case GFT_Real:
|
||||
GDALRATSetValueAsDouble( hRat, rowIdx, colIdx, row[ colIdx ].toDouble( ) );
|
||||
break;
|
||||
}
|
||||
case QVariant::Double:
|
||||
{
|
||||
fType = GFT_Real;
|
||||
case GFT_Integer:
|
||||
GDALRATSetValueAsInt( hRat, rowIdx, colIdx, row[ colIdx ].toInt( ) );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fType = GFT_String;
|
||||
GDALRATSetValueAsString( hRat, rowIdx, colIdx, row[ colIdx ].toString().toStdString().c_str() );
|
||||
}
|
||||
if ( GDALRATCreateColumn( hRat, field.name.toStdString().c_str(), fType, static_cast<GDALRATFieldUsage>( field.usage ) ) != CE_None )
|
||||
{
|
||||
if ( errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "RAT column '%1 could not be created, Raster Attribute Table was not saved (GDAL error)." ).arg( field.name );
|
||||
}
|
||||
GDALDestroyRasterAttributeTable( hRat );
|
||||
return false;
|
||||
}
|
||||
typeMap[ colIdx ] = fType;
|
||||
colIdx++;
|
||||
}
|
||||
rowIdx++;
|
||||
}
|
||||
|
||||
// Save data
|
||||
const QList<QVariantList> data { rat->data() };
|
||||
int rowIdx { 0 };
|
||||
for ( const auto &row : std::as_const( data ) )
|
||||
GDALRATSetTableType( hRat, static_cast<GDALRATTableType>( rat->type() ) );
|
||||
|
||||
if ( GDALSetDefaultRAT( hBand, hRat ) != CE_None )
|
||||
{
|
||||
if ( errorMessage )
|
||||
{
|
||||
for ( int colIdx = 0; colIdx < row.size(); colIdx++ )
|
||||
{
|
||||
switch ( typeMap[ colIdx ] )
|
||||
{
|
||||
case GFT_Real:
|
||||
GDALRATSetValueAsDouble( hRat, rowIdx, colIdx, row[ colIdx ].toDouble( ) );
|
||||
break;
|
||||
case GFT_Integer:
|
||||
GDALRATSetValueAsInt( hRat, rowIdx, colIdx, row[ colIdx ].toInt( ) );
|
||||
break;
|
||||
default:
|
||||
GDALRATSetValueAsString( hRat, rowIdx, colIdx, row[ colIdx ].toString().toStdString().c_str() );
|
||||
}
|
||||
}
|
||||
rowIdx++;
|
||||
*errorMessage = tr( "GDAL error saving raster attribute table, raster attribute table could not be saved." );
|
||||
}
|
||||
|
||||
GDALRATSetTableType( hRat, static_cast<GDALRATTableType>( rat->type() ) );
|
||||
|
||||
if ( GDALSetDefaultRAT( hBand, hRat ) != CE_None )
|
||||
{
|
||||
if ( errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "RAT could not be saved (GDAL error)." );
|
||||
}
|
||||
GDALDestroyRasterAttributeTable( hRat );
|
||||
return false;
|
||||
}
|
||||
|
||||
GDALDestroyRasterAttributeTable( hRat );
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
rat->setDirty( false );
|
||||
GDALFlushCache( mGdalBaseDataset );
|
||||
success = true;
|
||||
|
||||
}
|
||||
else if ( ! rat->isDirty() && errorMessage )
|
||||
{
|
||||
*errorMessage = QObject::tr( "RAT has not modifications and was not saved." );
|
||||
}
|
||||
}
|
||||
|
||||
if ( wasReopenedReadWrite )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read-only mode" ) );
|
||||
GDALClose( mGdalDataset );
|
||||
|
||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_READONLY );
|
||||
|
||||
// if the dataset couldn't be opened in read / write mode, tell the user
|
||||
if ( !mGdalBaseDataset )
|
||||
{
|
||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_UPDATE );
|
||||
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
||||
mGdalDataset = mGdalBaseDataset;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@ -231,7 +231,7 @@ class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase
|
||||
//! Load attribute tables
|
||||
bool readNativeAttributeTable( QString *errorMessage = nullptr ) override;
|
||||
|
||||
bool writeNativeAttributeTable( QString *errorMessage = nullptr ) const override; //#spellok
|
||||
bool writeNativeAttributeTable( QString *errorMessage = nullptr ) override; //#spellok
|
||||
|
||||
// There are 2 cloning mechanisms.
|
||||
// * Either the cloned provider use the same GDAL handles as the main provider
|
||||
|
||||
@ -36,10 +36,6 @@ Qgis::RasterAttributeTableType QgsRasterAttributeTable::type() const
|
||||
return mType;
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTable::setType( const Qgis::RasterAttributeTableType type )
|
||||
{
|
||||
mType = type;
|
||||
}
|
||||
|
||||
bool QgsRasterAttributeTable::hasColor() const
|
||||
{
|
||||
@ -324,13 +320,14 @@ bool QgsRasterAttributeTable::insertField( int position, const Field &field, QSt
|
||||
// Set/change the table type from the value field type
|
||||
if ( field.usage == Qgis::RasterAttributeTableFieldUsage::MinMax )
|
||||
{
|
||||
setType( Qgis::RasterAttributeTableType::Thematic );
|
||||
mType = Qgis::RasterAttributeTableType::Thematic;
|
||||
}
|
||||
else if ( field.usage == Qgis::RasterAttributeTableFieldUsage::Max || field.usage == Qgis::RasterAttributeTableFieldUsage::Max )
|
||||
{
|
||||
setType( Qgis::RasterAttributeTableType::Athematic );
|
||||
mType = Qgis::RasterAttributeTableType::Athematic;
|
||||
}
|
||||
|
||||
setType();
|
||||
setDirty( true );
|
||||
|
||||
return true;
|
||||
@ -356,6 +353,25 @@ bool QgsRasterAttributeTable::insertColor( int position, QString *errorMessage )
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsRasterAttributeTable::setFieldUsage( int fieldIndex, const Qgis::RasterAttributeTableFieldUsage usage )
|
||||
{
|
||||
if ( fieldIndex < 0 || fieldIndex >= fields().count( ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Field field { fields().at( fieldIndex ) };
|
||||
if ( ! usageInformation()[ usage ].allowedTypes.contains( field.type ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mFields[ fieldIndex ].usage = usage;
|
||||
setType();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsRasterAttributeTable::insertRamp( int position, QString *errorMessage )
|
||||
{
|
||||
if ( mType != Qgis::RasterAttributeTableType::Athematic )
|
||||
@ -402,6 +418,7 @@ bool QgsRasterAttributeTable::removeField( const QString &name, QString *errorMe
|
||||
{
|
||||
it->removeAt( idx );
|
||||
}
|
||||
setType();
|
||||
setDirty( true );
|
||||
return true;
|
||||
}
|
||||
@ -595,15 +612,6 @@ bool QgsRasterAttributeTable::readFromFile( const QString &path, QString *errorM
|
||||
appendRow( f.attributes().toList() );
|
||||
}
|
||||
|
||||
if ( usages().contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) )
|
||||
{
|
||||
setType( Qgis::RasterAttributeTableType::Thematic );
|
||||
}
|
||||
else
|
||||
{
|
||||
setType( Qgis::RasterAttributeTableType::Athematic );
|
||||
}
|
||||
|
||||
mFilePath = path;
|
||||
setDirty( false );
|
||||
|
||||
@ -1042,7 +1050,6 @@ QgsRasterAttributeTable *QgsRasterAttributeTable::createFromRaster( QgsRasterLay
|
||||
if ( const QgsPalettedRasterRenderer *palettedRenderer = dynamic_cast<const QgsPalettedRasterRenderer *>( renderer ) )
|
||||
{
|
||||
QgsRasterAttributeTable *rat = new QgsRasterAttributeTable();
|
||||
rat->setType( Qgis::RasterAttributeTableType::Thematic );
|
||||
rat->appendField( QStringLiteral( "Value" ), Qgis::RasterAttributeTableFieldUsage::MinMax, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Class" ), Qgis::RasterAttributeTableFieldUsage::Name, QVariant::Type::String );
|
||||
rat->appendField( QStringLiteral( "Red" ), Qgis::RasterAttributeTableFieldUsage::Red, QVariant::Type::Int );
|
||||
@ -1071,38 +1078,92 @@ QgsRasterAttributeTable *QgsRasterAttributeTable::createFromRaster( QgsRasterLay
|
||||
if ( const QgsColorRampShader *shaderFunction = dynamic_cast<const QgsColorRampShader *>( shader->rasterShaderFunction() ) )
|
||||
{
|
||||
QgsRasterAttributeTable *rat = new QgsRasterAttributeTable();
|
||||
rat->setType( Qgis::RasterAttributeTableType::Athematic );
|
||||
rat->appendField( QStringLiteral( "Min" ), Qgis::RasterAttributeTableFieldUsage::Min, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Max" ), Qgis::RasterAttributeTableFieldUsage::Max, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Class" ), Qgis::RasterAttributeTableFieldUsage::Name, QVariant::Type::String );
|
||||
rat->appendField( QStringLiteral( "RedMin" ), Qgis::RasterAttributeTableFieldUsage::RedMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "GreenMin" ), Qgis::RasterAttributeTableFieldUsage::GreenMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "BlueMin" ), Qgis::RasterAttributeTableFieldUsage::BlueMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "AlphaMin" ), Qgis::RasterAttributeTableFieldUsage::AlphaMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "RedMax" ), Qgis::RasterAttributeTableFieldUsage::RedMax, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "GreenMax" ), Qgis::RasterAttributeTableFieldUsage::GreenMax, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "BlueMax" ), Qgis::RasterAttributeTableFieldUsage::BlueMax, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "AlphaMax" ), Qgis::RasterAttributeTableFieldUsage::AlphaMax, QVariant::Type::Int );
|
||||
const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
|
||||
if ( rampItems.count( ) > 1 )
|
||||
switch ( shaderFunction->colorRampType() )
|
||||
{
|
||||
QColor color1 { rampItems.at( 0 ).color };
|
||||
QString label1 { rampItems.at( 0 ).label };
|
||||
QVariant value1( rampItems.at( 0 ).value );
|
||||
for ( int i = 1; i < rampItems.count( ); ++i )
|
||||
|
||||
case QgsColorRampShader::Type::Interpolated:
|
||||
{
|
||||
const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i )};
|
||||
rat->appendRow( QVariantList() << value1 << rampItem.value << QStringLiteral( "%1 - %2" ).arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
|
||||
rat->setRamp( rat->data().length() - 1, color1, rampItem.color );
|
||||
label1 = rampItem.label;
|
||||
value1 = rampItem.value;
|
||||
color1 = rampItem.color;
|
||||
rat->appendField( QStringLiteral( "Min" ), Qgis::RasterAttributeTableFieldUsage::Min, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Max" ), Qgis::RasterAttributeTableFieldUsage::Max, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Class" ), Qgis::RasterAttributeTableFieldUsage::Name, QVariant::Type::String );
|
||||
rat->appendField( QStringLiteral( "RedMin" ), Qgis::RasterAttributeTableFieldUsage::RedMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "GreenMin" ), Qgis::RasterAttributeTableFieldUsage::GreenMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "BlueMin" ), Qgis::RasterAttributeTableFieldUsage::BlueMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "AlphaMin" ), Qgis::RasterAttributeTableFieldUsage::AlphaMin, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "RedMax" ), Qgis::RasterAttributeTableFieldUsage::RedMax, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "GreenMax" ), Qgis::RasterAttributeTableFieldUsage::GreenMax, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "BlueMax" ), Qgis::RasterAttributeTableFieldUsage::BlueMax, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "AlphaMax" ), Qgis::RasterAttributeTableFieldUsage::AlphaMax, QVariant::Type::Int );
|
||||
const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
|
||||
if ( rampItems.count( ) > 1 )
|
||||
{
|
||||
QColor color1 { rampItems.at( 0 ).color };
|
||||
QString label1 { rampItems.at( 0 ).label };
|
||||
QVariant value1( rampItems.at( 0 ).value );
|
||||
for ( int i = 1; i < rampItems.count( ); ++i )
|
||||
{
|
||||
const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i )};
|
||||
rat->appendRow( QVariantList() << value1 << rampItem.value << QStringLiteral( "%1 - %2" ).arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
|
||||
rat->setRamp( rat->data().length() - 1, color1, rampItem.color );
|
||||
label1 = rampItem.label;
|
||||
value1 = rampItem.value;
|
||||
color1 = rampItem.color;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsColorRampShader::Type::Discrete:
|
||||
{
|
||||
rat->appendField( QStringLiteral( "Min" ), Qgis::RasterAttributeTableFieldUsage::Min, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Max" ), Qgis::RasterAttributeTableFieldUsage::Max, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Class" ), Qgis::RasterAttributeTableFieldUsage::Name, QVariant::Type::String );
|
||||
rat->appendField( QStringLiteral( "Red" ), Qgis::RasterAttributeTableFieldUsage::Red, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "Green" ), Qgis::RasterAttributeTableFieldUsage::Green, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "Blue" ), Qgis::RasterAttributeTableFieldUsage::Blue, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "Alpha" ), Qgis::RasterAttributeTableFieldUsage::Alpha, QVariant::Type::Int );
|
||||
const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
|
||||
if ( rampItems.count( ) > 1 )
|
||||
{
|
||||
QColor color1 { rampItems.at( 0 ).color };
|
||||
QString label1 { rampItems.at( 0 ).label };
|
||||
QVariant value1( rampItems.at( 0 ).value );
|
||||
for ( int i = 1; i < rampItems.count( ); ++i )
|
||||
{
|
||||
const QgsColorRampShader::ColorRampItem &rampItem { rampItems.at( i )};
|
||||
rat->appendRow( QVariantList() << value1 << rampItem.value << QStringLiteral( "%1 - %2" ).arg( label1, rampItem.label ) << 0 << 0 << 0 << 255 << 0 << 0 << 0 << 255 );
|
||||
rat->setRamp( rat->data().length() - 1, color1, rampItem.color );
|
||||
label1 = rampItem.label;
|
||||
value1 = rampItem.value;
|
||||
color1 = rampItem.color;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsColorRampShader::Type::Exact:
|
||||
{
|
||||
rat->appendField( QStringLiteral( "Value" ), Qgis::RasterAttributeTableFieldUsage::MinMax, QVariant::Type::Double );
|
||||
rat->appendField( QStringLiteral( "Class" ), Qgis::RasterAttributeTableFieldUsage::Name, QVariant::Type::String );
|
||||
rat->appendField( QStringLiteral( "Red" ), Qgis::RasterAttributeTableFieldUsage::Red, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "Green" ), Qgis::RasterAttributeTableFieldUsage::Green, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "Blue" ), Qgis::RasterAttributeTableFieldUsage::Blue, QVariant::Type::Int );
|
||||
rat->appendField( QStringLiteral( "Alpha" ), Qgis::RasterAttributeTableFieldUsage::Alpha, QVariant::Type::Int );
|
||||
const QList<QgsColorRampShader::ColorRampItem> rampItems { shaderFunction->colorRampItemList() };
|
||||
for ( const QgsColorRampShader::ColorRampItem &rampItem : std::as_const( rampItems ) )
|
||||
{
|
||||
rat->appendRow( QVariantList() << rampItem.value << rampItem.label << 0 << 0 << 0 << 255 );
|
||||
rat->setColor( rat->data().length() - 1, rampItem.color );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bandNumber )
|
||||
{
|
||||
*bandNumber = pseudoColorRenderer->band();
|
||||
}
|
||||
|
||||
return rat;
|
||||
}
|
||||
else
|
||||
@ -1149,6 +1210,12 @@ QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInform
|
||||
return QgsRasterAttributeTable::sUsageInformation;
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTable::setType()
|
||||
{
|
||||
const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
|
||||
mType = fieldUsages.contains( Qgis::RasterAttributeTableFieldUsage::MinMax ) ? Qgis::RasterAttributeTableType::Thematic : Qgis::RasterAttributeTableType::Athematic;
|
||||
}
|
||||
|
||||
QHash<int, QgsRasterAttributeTable::UsageInformation> QgsRasterAttributeTable::usageInformationInt()
|
||||
{
|
||||
QHash<int, QgsRasterAttributeTable::UsageInformation> usageInfoInt;
|
||||
@ -1275,11 +1342,7 @@ QgsGradientColorRamp QgsRasterAttributeTable::colorRamp( QStringList &labels, co
|
||||
|
||||
if ( ! isnan( min ) && ! isnan( max ) )
|
||||
{
|
||||
QList<QVariantList> dataCopy { mData };
|
||||
std::sort( dataCopy.begin(), dataCopy.end(), [ & ]( const QVariantList & first, const QVariantList & second ) -> bool
|
||||
{
|
||||
return ( first.at( maxIdx ).toDouble() + first.at( minIdx ).toDouble() ) / 2 < ( second.at( maxIdx ).toDouble() + second.at( minIdx ).toDouble() ) / 2;
|
||||
} );
|
||||
const QList<QVariantList> dataCopy( orderedRows() );
|
||||
|
||||
QgsRasterAttributeTable orderedRat;
|
||||
for ( const Field &f : std::as_const( mFields ) )
|
||||
@ -1336,7 +1399,7 @@ QgsGradientColorRamp QgsRasterAttributeTable::colorRamp( QStringList &labels, co
|
||||
};
|
||||
|
||||
// Case 1: range classes, discrete colors
|
||||
// Create stops for the min value of each class except for the first.
|
||||
// Create stops for the lower value of each class except for the first.
|
||||
if ( orderedRat.hasColor() && isRange )
|
||||
{
|
||||
labels.push_back( labelFromField( 0 ) );
|
||||
@ -1350,57 +1413,47 @@ QgsGradientColorRamp QgsRasterAttributeTable::colorRamp( QStringList &labels, co
|
||||
}
|
||||
}
|
||||
// Case 2: range classes, gradients colors
|
||||
// Take the class borders (average value between max of previous class and min of the next)
|
||||
// Take the class bounds (average value between max of previous class and min of the next)
|
||||
// to avoid potential overlapping or gaps between classes.
|
||||
// Create stop:
|
||||
// first stop at value taking the max color of the previous class
|
||||
// second stop at value + epsilon taking the min color of the next class
|
||||
// second stop at value + epsilon taking the min color of the next class, unless colors and offset are equal
|
||||
else if ( orderedRat.hasRamp() && isRange )
|
||||
{
|
||||
double prevOffset { 0 };
|
||||
labels.push_back( labelFromField( 0 ) );
|
||||
for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
|
||||
{
|
||||
labels.push_back( labelFromField( rowIdx ) );
|
||||
const int prevRowIdx { rowIdx - 1 };
|
||||
const double offset { ( ( orderedRat.value( rowIdx, minIdx ).toDouble( ) + orderedRat.value( prevRowIdx, maxIdx ).toDouble( ) ) / 2.0 - min ) / range };
|
||||
const QgsGradientColorRamp previousRamp { orderedRat.ramp( prevRowIdx ) };
|
||||
stops.append( QgsGradientStop( offset, previousRamp.color2() ) );
|
||||
|
||||
const QgsGradientColorRamp currentRamp { orderedRat.ramp( rowIdx ) };
|
||||
stops.append( QgsGradientStop( offset + std::numeric_limits<double>::epsilon(), currentRamp.color1() ) );
|
||||
// An additional stop is added if the colors are different or offsets are different by 1e-6 (offset varies from 0 to 1).
|
||||
if ( currentRamp.color1() != previousRamp.color2() && qgsDoubleNear( offset, prevOffset, 1e-6 ) )
|
||||
{
|
||||
stops.append( QgsGradientStop( offset + std::numeric_limits<double>::epsilon(), currentRamp.color1() ) );
|
||||
}
|
||||
prevOffset = offset;
|
||||
}
|
||||
}
|
||||
// Case 3: range classes but no colors at all
|
||||
// Take the class borders (average value between max of previous class and min of the next)
|
||||
// Create stop for the lower class, actually skipping the upper bound of the last class
|
||||
else
|
||||
{
|
||||
labels.push_back( labelFromField( 0 ) );
|
||||
|
||||
for ( int rowIdx = 1; rowIdx < orderedRat.data().count(); ++rowIdx )
|
||||
{
|
||||
const int prevRowIdx { rowIdx - 1 };
|
||||
const double offset { ( ( orderedRat.value( rowIdx, minIdx ).toDouble( ) + orderedRat.value( prevRowIdx, maxIdx ).toDouble( ) ) / 2.0 - min ) / range };
|
||||
stops.append( QgsGradientStop( offset, ramp.color( offset ) ) );
|
||||
labels.push_back( labelFromField( rowIdx ) );
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Collect stops and colors
|
||||
for ( int rowIdx = 0; rowIdx < orderedRat.data().count(); ++rowIdx )
|
||||
{
|
||||
|
||||
const double offset { ( orderedRat.value( rowIdx, maxIdx ).toDouble( ) - min ) / range };
|
||||
|
||||
if ( hasColor( ) && rowIdx < orderedRat.data().count() - 1 )
|
||||
{
|
||||
const QColor color { orderedRat.color( rowIdx ) };
|
||||
stops.append( QgsGradientStop( offset, color ) );
|
||||
}
|
||||
else if ( hasRamp() && rowIdx < orderedRat.data().count() - 1 )
|
||||
{
|
||||
const QColor color1 { orderedRat.ramp( rowIdx ).color1() };
|
||||
if ( color1 != lastColor )
|
||||
{
|
||||
stops.append( QgsGradientStop( lastOffset + std::numeric_limits<double>::epsilon(), color1 ) );
|
||||
}
|
||||
const QColor color2 { orderedRat.ramp( rowIdx ).color2() };
|
||||
lastColor = color2;
|
||||
stops.append( QgsGradientStop( offset, color1 ) );
|
||||
}
|
||||
else if ( ! hasColor() && ! hasRamp() )
|
||||
{
|
||||
stops.push_back( QgsGradientStop( offset, ramp.color( offset ) ) );
|
||||
}
|
||||
lastOffset = offset;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1461,9 +1514,6 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
||||
// Set labels
|
||||
if ( QgsColorRampShader *shaderFunction = static_cast<QgsColorRampShader *>( pseudoColorRenderer->shader()->rasterShaderFunction() ) )
|
||||
{
|
||||
QgsColorRampLegendNodeSettings *settings = new QgsColorRampLegendNodeSettings( *( shaderFunction->legendSettings() ) );
|
||||
settings->setUseContinuousLegend( false );
|
||||
shaderFunction->setLegendSettings( settings );
|
||||
shaderFunction->setMinimumValue( minValue() );
|
||||
shaderFunction->setMaximumValue( maxValue() );
|
||||
const QList<QgsColorRampShader::ColorRampItem> itemList { shaderFunction->colorRampItemList() };
|
||||
@ -1471,9 +1521,11 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
||||
if ( ! itemList.isEmpty() )
|
||||
{
|
||||
QList<QgsColorRampShader::ColorRampItem> deduplicatedDitemList;
|
||||
// Deduplicate entries, this is necessary because the ramp for athematic
|
||||
// Deduplicate entries, this is necessary when the ramp for athematic
|
||||
// ramp rasters creates a stop for each class limit (min and max) which
|
||||
// should be coincident with the previous class max and next class min.
|
||||
// may be coincident with the previous class max and next class min.
|
||||
// When this happens and the colors for the previous class max and
|
||||
// next class min are equal we can squash the legend classes.
|
||||
QgsColorRampShader::ColorRampItem item { itemList.first() };
|
||||
if ( labelsAreUsable )
|
||||
{
|
||||
@ -1504,6 +1556,40 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
||||
return renderer;
|
||||
}
|
||||
|
||||
QList<QList<QVariant> > QgsRasterAttributeTable::orderedRows() const
|
||||
{
|
||||
const QList<Qgis::RasterAttributeTableFieldUsage> fieldUsages { usages() };
|
||||
const int minIdx { fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Min ) };
|
||||
const int maxIdx { fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::Max ) };
|
||||
const bool isRange { minIdx >= 0 && maxIdx >= 0 };
|
||||
QList<QVariantList> dataCopy( mData );
|
||||
|
||||
if ( isRange )
|
||||
{
|
||||
std::sort( dataCopy.begin(), dataCopy.end(), [ & ]( const QVariantList & first, const QVariantList & second ) -> bool
|
||||
{
|
||||
return ( first.at( maxIdx ).toDouble() + first.at( minIdx ).toDouble() ) < ( second.at( maxIdx ).toDouble() + second.at( minIdx ).toDouble() );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
const int minMaxIdx { fieldUsages.indexOf( Qgis::RasterAttributeTableFieldUsage::MinMax ) };
|
||||
if ( minMaxIdx < 0 )
|
||||
{
|
||||
return dataCopy;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort( dataCopy.begin(), dataCopy.end(), [ & ]( const QVariantList & first, const QVariantList & second ) -> bool
|
||||
{
|
||||
return first.at( minMaxIdx ).toDouble() < second.at( minMaxIdx ).toDouble();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
return dataCopy;
|
||||
}
|
||||
|
||||
bool QgsRasterAttributeTable::Field::isColor() const
|
||||
{
|
||||
return usage == Qgis::RasterAttributeTableFieldUsage::Red || usage == Qgis::RasterAttributeTableFieldUsage::Green || usage == Qgis::RasterAttributeTableFieldUsage::Blue || usage == Qgis::RasterAttributeTableFieldUsage::Alpha;
|
||||
|
||||
@ -104,11 +104,6 @@ class CORE_EXPORT QgsRasterAttributeTable
|
||||
*/
|
||||
Qgis::RasterAttributeTableType type() const;
|
||||
|
||||
/**
|
||||
* Sets the Raster Attribute Table \a type
|
||||
*/
|
||||
void setType( const Qgis::RasterAttributeTableType type );
|
||||
|
||||
/**
|
||||
* Returns TRUE if the Raster Attribute Table has color RGBA information.
|
||||
* \see color()
|
||||
@ -230,6 +225,12 @@ class CORE_EXPORT QgsRasterAttributeTable
|
||||
*/
|
||||
bool insertColor( int position, QString *errorMessage SIP_OUT = nullptr );
|
||||
|
||||
/**
|
||||
* Change the usage of the field at index \a fieldIndex to \a usage with checks for allowed types.
|
||||
* \return TRUE on success.
|
||||
*/
|
||||
bool setFieldUsage( int fieldIndex, const Qgis::RasterAttributeTableFieldUsage usage );
|
||||
|
||||
/**
|
||||
* Create RGBA minimum and maximum fields and inserts them at \a position, optionally reporting any error in \a errorMessage, returns TRUE on success.
|
||||
*/
|
||||
@ -331,18 +332,31 @@ class CORE_EXPORT QgsRasterAttributeTable
|
||||
|
||||
/**
|
||||
* Returns the color ramp for an athematic Raster Attribute Table
|
||||
* returning the \a labels, optionally generated from \a labelColumn.
|
||||
* setting the labels in \a labels, optionally generated from \a labelColumn.
|
||||
*/
|
||||
QgsGradientColorRamp colorRamp( QStringList &labels SIP_OUT, const int labelColumn = -1 ) const;
|
||||
|
||||
/**
|
||||
* Creates and returns a (possibly NULLPTR) raster renderer for the
|
||||
* specified \a provider and \a bandNumber and optionally classified
|
||||
* specified \a provider and \a bandNumber and optionally reclassified
|
||||
* by \a classificationColumn, the default value of -1 makes the method
|
||||
* guess the classification column based on the field usage.
|
||||
*
|
||||
* \note athematic attribute tables with color ramps cannot be reclassified,
|
||||
* the renderer will still use the \a classificationColumn for
|
||||
* generating the class labels.
|
||||
*/
|
||||
QgsRasterRenderer *createRenderer( QgsRasterDataProvider *provider, const int bandNumber, const int classificationColumn = -1 ) SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns the data rows ordered by the value column(s) in ascending order, if
|
||||
* the attribute table type is athematic the middle value for each row range
|
||||
* is considered for ordering.
|
||||
* If the attribute table does not have any value field (and hence is not valid),
|
||||
* the current data are returned without any change.
|
||||
*/
|
||||
QList<QList<QVariant>> orderedRows( ) const;
|
||||
|
||||
/**
|
||||
* Try to determine the field usage from its \a name and \a type.
|
||||
*/
|
||||
@ -387,7 +401,7 @@ class CORE_EXPORT QgsRasterAttributeTable
|
||||
*/
|
||||
static QHash<int, QgsRasterAttributeTable::UsageInformation> usageInformationInt( ) SIP_PYNAME( usageInformation );
|
||||
|
||||
static QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> sUsageInformation;
|
||||
static QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> sUsageInformation SIP_SKIP;
|
||||
|
||||
///@encond
|
||||
|
||||
@ -399,6 +413,9 @@ class CORE_EXPORT QgsRasterAttributeTable
|
||||
bool mIsDirty;
|
||||
QString mFilePath;
|
||||
|
||||
// Set type from fields.
|
||||
void setType( );
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSRASTERATTRIBUTETABLE_H
|
||||
|
||||
@ -714,7 +714,7 @@ bool QgsRasterDataProvider::readFileBasedAttributeTable( int bandNumber, const Q
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsRasterDataProvider::writeNativeAttributeTable( QString *errorMessage ) const //#spellok
|
||||
bool QgsRasterDataProvider::writeNativeAttributeTable( QString *errorMessage ) //#spellok
|
||||
{
|
||||
Q_UNUSED( errorMessage );
|
||||
return false;
|
||||
|
||||
@ -793,7 +793,7 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
|
||||
* \note No checks for Raster Attribute Table validity are performed when saving, it is client code responsibility to handle validation.
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
virtual bool writeNativeAttributeTable( QString *errorMessage SIP_OUT = nullptr ) const; //#spellok
|
||||
virtual bool writeNativeAttributeTable( QString *errorMessage SIP_OUT = nullptr ); //#spellok
|
||||
|
||||
/**
|
||||
* Reads the native attribute table, optionally reporting any error in \a errorMessage, returns TRUE on success.
|
||||
|
||||
@ -149,11 +149,17 @@ QgsRasterRenderer *QgsRasterRendererRegistry::defaultRendererForDrawingStyle( Qg
|
||||
const int grayBand = 1;
|
||||
|
||||
// If the raster band has an attribute table try to use it.
|
||||
if ( QgsRasterAttributeTable *rat = provider->attributeTable( grayBand ) )
|
||||
QString ratErrorMessage;
|
||||
if ( QgsRasterAttributeTable *rat = provider->attributeTable( grayBand ); rat && rat->isValid( &ratErrorMessage ) )
|
||||
{
|
||||
renderer = rat->createRenderer( provider, grayBand );
|
||||
}
|
||||
|
||||
if ( ! ratErrorMessage.isEmpty() )
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "Invalid RAT from band 1, RAT was not used to create the renderer: %1." ).arg( ratErrorMessage ), 2 );
|
||||
}
|
||||
|
||||
if ( ! renderer )
|
||||
{
|
||||
renderer = new QgsSingleBandGrayRenderer( provider, grayBand );
|
||||
|
||||
@ -3,6 +3,7 @@ set(QGIS_GUI_SRCS
|
||||
raster/qgsrasterattributetablewidget.cpp
|
||||
raster/qgsrasterattributetabledialog.cpp
|
||||
raster/qgsrasterattributetableaddcolumndialog.cpp
|
||||
raster/qgsrasterattributetableaddrowdialog.cpp
|
||||
raster/qgscolorrampshaderwidget.cpp
|
||||
raster/qgsmultibandcolorrendererwidget.cpp
|
||||
raster/qgspalettedrendererwidget.cpp
|
||||
@ -1287,6 +1288,7 @@ set(QGIS_GUI_HDRS
|
||||
raster/qgscreaterasterattributetabledialog.h
|
||||
raster/qgsrasterattributetabledialog.h
|
||||
raster/qgsrasterattributetableaddcolumndialog.h
|
||||
raster/qgsrasterattributetableaddrowdialog.h
|
||||
raster/qgscolorrampshaderwidget.h
|
||||
raster/qgshillshaderendererwidget.h
|
||||
raster/qgsmultibandcolorrendererwidget.h
|
||||
@ -1451,6 +1453,7 @@ set(QGIS_GUI_UI_HDRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsqueryresultwidgetbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsrasterattributetablewidgetbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsrasterattributetabledialogbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsrasterattributetableaddrowdialogbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgscreaterasterattributetabledialogbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsrasterattributetableaddcolumndialog.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssqlcomposerdialogbase.h
|
||||
|
||||
@ -25,24 +25,13 @@ QgsCreateRasterAttributeTableDialog::QgsCreateRasterAttributeTableDialog( QgsRas
|
||||
|
||||
setupUi( this );
|
||||
|
||||
const bool nativeRatSupported { mRasterLayer->dataProvider()->providerCapabilities().testFlag( QgsRasterDataProvider::ProviderCapability::NativeRasterAttributeTable ) };
|
||||
// Apparently, some drivers (HFA) ignore Min/Max fields and set them to generic,
|
||||
// for this reason we disable the native support for thematic RATs (later in the loop)
|
||||
bool nativeRatSupported { mRasterLayer->dataProvider()->providerCapabilities().testFlag( QgsRasterDataProvider::ProviderCapability::NativeRasterAttributeTable ) };
|
||||
|
||||
connect( mNativeRadioButton, &QRadioButton::toggled, this, &QgsCreateRasterAttributeTableDialog::updateButtons );
|
||||
connect( mDbfRadioButton, &QRadioButton::toggled, this, &QgsCreateRasterAttributeTableDialog::updateButtons );
|
||||
|
||||
if ( ! nativeRatSupported )
|
||||
{
|
||||
mNativeRadioButton->setEnabled( false );
|
||||
mDbfRadioButton->setChecked( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDbfPathWidget->setFilter( QStringLiteral( "VAT DBF Files (*.vat.dbf)" ) );
|
||||
if ( QFile::exists( mRasterLayer->dataProvider()->dataSourceUri( ) ) )
|
||||
{
|
||||
mDbfPathWidget->setFilePath( mRasterLayer->dataProvider()->dataSourceUri( ) + ".vat.dbf" );
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing rats
|
||||
QStringList existingRatsInfo;
|
||||
@ -52,6 +41,12 @@ QgsCreateRasterAttributeTableDialog::QgsCreateRasterAttributeTableDialog( QgsRas
|
||||
{
|
||||
if ( QgsRasterAttributeTable *rat = mRasterLayer->attributeTable( bandNo ) )
|
||||
{
|
||||
// disable the native support for thematic RATs
|
||||
if ( nativeRatSupported && rat->type() != Qgis::RasterAttributeTableType::Athematic )
|
||||
{
|
||||
nativeRatSupported = false;
|
||||
existingRatsInfo.push_back( tr( "The data provider supports attribute table storage but some drivers do not support 'thematic' types, for this reason the option is disabled." ) );
|
||||
}
|
||||
if ( ! rat->filePath().isEmpty() )
|
||||
{
|
||||
existingRatsInfo.push_back( tr( "Raster band %1 already has an associated attribute table at %2." ).arg( QString::number( bandNo ), rat->filePath() ) );
|
||||
@ -67,10 +62,27 @@ QgsCreateRasterAttributeTableDialog::QgsCreateRasterAttributeTableDialog( QgsRas
|
||||
if ( ! existingRatsInfo.isEmpty() )
|
||||
{
|
||||
mCreateInfoLabel->setText( mCreateInfoLabel->text().append( QStringLiteral( "<br><ul><li>" ) + existingRatsInfo.join( QStringLiteral( "</li><li>" ) ) ).append( QStringLiteral( "</ul>" ) ) );
|
||||
mCreateInfoLabel->show();
|
||||
}
|
||||
|
||||
if ( ! nativeRatSupported )
|
||||
{
|
||||
mNativeRadioButton->setEnabled( false );
|
||||
mDbfRadioButton->setChecked( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDbfPathWidget->setFilter( QStringLiteral( "VAT DBF Files (*.vat.dbf)" ) );
|
||||
if ( QFile::exists( mRasterLayer->dataProvider()->dataSourceUri( ) ) )
|
||||
{
|
||||
mDbfPathWidget->setFilePath( mRasterLayer->dataProvider()->dataSourceUri( ) + ".vat.dbf" );
|
||||
}
|
||||
}
|
||||
|
||||
connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
|
||||
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
QString QgsCreateRasterAttributeTableDialog::filePath() const
|
||||
@ -78,6 +90,11 @@ QString QgsCreateRasterAttributeTableDialog::filePath() const
|
||||
return mDbfPathWidget->filePath();
|
||||
}
|
||||
|
||||
bool QgsCreateRasterAttributeTableDialog::saveToFile() const
|
||||
{
|
||||
return mDbfRadioButton->isChecked();
|
||||
}
|
||||
|
||||
bool QgsCreateRasterAttributeTableDialog::openWhenDone() const
|
||||
{
|
||||
return mOpenRat->isChecked();
|
||||
|
||||
@ -48,6 +48,11 @@ class GUI_EXPORT QgsCreateRasterAttributeTableDialog : public QDialog, private U
|
||||
*/
|
||||
QString filePath( ) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the option to save to a file is selected.
|
||||
*/
|
||||
bool saveToFile( ) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the option to open the newly created attribute table is checked.
|
||||
*/
|
||||
|
||||
@ -23,11 +23,20 @@
|
||||
|
||||
class QgsRasterAttributeTable;
|
||||
|
||||
/**
|
||||
* The QgsRasterAttributeTableAddColumnDialog class collects options to add a new column to a raster attribute table.
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
class GUI_EXPORT QgsRasterAttributeTableAddColumnDialog : public QDialog, private Ui::QgsRasterAttributeTableAddColumnDialogBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates a new QgsRasterAttributeTableAddColumnDialog
|
||||
* \param attributeTable the raster attribute table
|
||||
* \param parent optional parent
|
||||
*/
|
||||
QgsRasterAttributeTableAddColumnDialog( QgsRasterAttributeTable *attributeTable, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
|
||||
30
src/gui/raster/qgsrasterattributetableaddrowdialog.cpp
Normal file
30
src/gui/raster/qgsrasterattributetableaddrowdialog.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/***************************************************************************
|
||||
qgsrasterattributetableaddrowdialog.cpp - QgsRasterAttributeTableAddRowDialog
|
||||
|
||||
---------------------
|
||||
begin : 18.10.2022
|
||||
copyright : (C) 2022 by ale
|
||||
email : [your-email-here]
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsrasterattributetableaddrowdialog.h"
|
||||
#include "qgsgui.h"
|
||||
|
||||
QgsRasterAttributeTableAddRowDialog::QgsRasterAttributeTableAddRowDialog( QWidget *parent )
|
||||
: QDialog( parent )
|
||||
{
|
||||
setupUi( this );
|
||||
QgsGui::enableAutoGeometryRestore( this );
|
||||
|
||||
}
|
||||
|
||||
bool QgsRasterAttributeTableAddRowDialog::insertAfter() const
|
||||
{
|
||||
return mAfter->isChecked();
|
||||
}
|
||||
48
src/gui/raster/qgsrasterattributetableaddrowdialog.h
Normal file
48
src/gui/raster/qgsrasterattributetableaddrowdialog.h
Normal file
@ -0,0 +1,48 @@
|
||||
/***************************************************************************
|
||||
qgsrasterattributetableaddrowdialog.h - QgsRasterAttributeTableAddRowDialog
|
||||
|
||||
---------------------
|
||||
begin : 18.10.2022
|
||||
copyright : (C) 2022 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSRASTERATTRIBUTETABLEADDROWDIALOG_H
|
||||
#define QGSRASTERATTRIBUTETABLEADDROWDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "qgis_gui.h"
|
||||
#include "qgis.h"
|
||||
#include "ui_qgsrasterattributetableaddrowdialogbase.h"
|
||||
|
||||
/**
|
||||
* The QgsRasterAttributeTableAddColumnDialog class collects options to add a new row to a raster attribute table.
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
class GUI_EXPORT QgsRasterAttributeTableAddRowDialog : public QDialog, private Ui::QgsRasterAttributeTableAddRowDialogBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates a new QgsRasterAttributeTableAddRowDialog
|
||||
* \param attributeTable the raster attribute table
|
||||
* \param parent optional parent
|
||||
*/
|
||||
QgsRasterAttributeTableAddRowDialog( QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
* Returns TRUE if the desired insertion position for the new row is after the currently selected row, FALSE if the insertion point is before.
|
||||
*/
|
||||
bool insertAfter() const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSRASTERATTRIBUTETABLEADDROWDIALOG_H
|
||||
@ -15,6 +15,7 @@
|
||||
***************************************************************************/
|
||||
#include "qgsrasterattributetablemodel.h"
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
|
||||
|
||||
QString QgsRasterAttributeTableModel::RAT_COLOR_HEADER_NAME = QObject::tr( "Color" );
|
||||
@ -392,6 +393,12 @@ QVariant QgsRasterAttributeTableModel::data( const QModelIndex &index, int role
|
||||
{
|
||||
return mRat->data().at( index.row() ).at( index.column() );
|
||||
}
|
||||
else if ( role == Qt::ItemDataRole::FontRole && ( field.isColor() || field.isRamp() ) )
|
||||
{
|
||||
QFont font;
|
||||
font.setItalic( true );
|
||||
return font;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsmessagebar.h"
|
||||
#include "qgsrasterattributetableaddcolumndialog.h"
|
||||
#include "qgsrasterattributetableaddrowdialog.h"
|
||||
#include "qgscolorbutton.h"
|
||||
#include "qgsgradientcolorrampdialog.h"
|
||||
#include "qgspalettedrasterrenderer.h"
|
||||
@ -99,7 +100,6 @@ void QgsRasterAttributeTableWidget::init( int bandNumber )
|
||||
mAttributeTable = nullptr;
|
||||
mCurrentBand = 0;
|
||||
mRasterBandsComboBox->clear();
|
||||
mClassifyComboBox->clear();
|
||||
|
||||
QList<int> availableRats;
|
||||
|
||||
@ -141,42 +141,18 @@ void QgsRasterAttributeTableWidget::init( int bandNumber )
|
||||
updateButtons();
|
||||
} );
|
||||
|
||||
connect( mModel.get(), &QgsRasterAttributeTableModel::columnsInserted, this, [ = ]( const QModelIndex &, int, int )
|
||||
{
|
||||
setDelegates();
|
||||
} );
|
||||
|
||||
connect( mModel.get(), &QgsRasterAttributeTableModel::columnsRemoved, this, [ = ]( const QModelIndex &, int, int )
|
||||
{
|
||||
setDelegates();
|
||||
} );
|
||||
|
||||
static_cast<QSortFilterProxyModel *>( mRATView->model() )->setSourceModel( mModel.get() );
|
||||
|
||||
const QList<QgsRasterAttributeTable::Field> tableFields { mAttributeTable->fields() };
|
||||
int fieldIdx { 0 };
|
||||
const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
|
||||
for ( const QgsRasterAttributeTable::Field &f : std::as_const( tableFields ) )
|
||||
{
|
||||
if ( usageInfo[f.usage].maybeClass )
|
||||
{
|
||||
mClassifyComboBox->addItem( QgsFields::iconForFieldType( f.type ), f.name, QVariant( fieldIdx ) );
|
||||
}
|
||||
fieldIdx++;
|
||||
}
|
||||
|
||||
if ( mAttributeTable->hasColor() )
|
||||
{
|
||||
if ( mAttributeTable->usages().contains( Qgis::RasterAttributeTableFieldUsage::Alpha ) )
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorAlphaDelegate( mRATView ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorDelegate( mRATView ) );
|
||||
}
|
||||
}
|
||||
else if ( mAttributeTable->hasRamp() )
|
||||
{
|
||||
if ( mAttributeTable->usages().contains( Qgis::RasterAttributeTableFieldUsage::AlphaMin ) )
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorRampAlphaDelegate( mRATView ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorRampDelegate( mRATView ) );
|
||||
}
|
||||
}
|
||||
setDelegates();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -250,7 +226,7 @@ void QgsRasterAttributeTableWidget::saveChanges()
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( nativeRatSupported )
|
||||
else if ( newPath.isEmpty() )
|
||||
{
|
||||
saveToNative = true;
|
||||
}
|
||||
@ -371,8 +347,29 @@ void QgsRasterAttributeTableWidget::addRow()
|
||||
{
|
||||
if ( mAttributeTable )
|
||||
{
|
||||
// Default to append
|
||||
int position { mModel->rowCount( QModelIndex() ) };
|
||||
const QModelIndex currentIndex { mProxyModel->mapToSource( mRATView->selectionModel()->currentIndex() ) };
|
||||
|
||||
// If there is a selected row, ask if before of after.
|
||||
if ( currentIndex.isValid() )
|
||||
{
|
||||
// Ask the user where to insert the new row (before or after the currently
|
||||
// selected row).
|
||||
QgsRasterAttributeTableAddRowDialog dlg;
|
||||
if ( dlg.exec() != QDialog::DialogCode::Accepted )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = currentIndex.row() + ( dlg.insertAfter() ? 1 : 0 );
|
||||
}
|
||||
}
|
||||
|
||||
bool result { true };
|
||||
QString errorMessage;
|
||||
|
||||
QVariantList rowData;
|
||||
|
||||
QList<QgsRasterAttributeTable::Field> fields { mAttributeTable->fields() };
|
||||
@ -381,19 +378,16 @@ void QgsRasterAttributeTableWidget::addRow()
|
||||
rowData.push_back( QVariant( field.type ) );
|
||||
}
|
||||
|
||||
const QModelIndex currentIndex { mProxyModel->mapToSource( mRATView->selectionModel()->currentIndex() ) };
|
||||
if ( currentIndex.isValid() )
|
||||
{
|
||||
result = mModel->insertRow( currentIndex.row(), rowData, &errorMessage );
|
||||
}
|
||||
else
|
||||
{
|
||||
result = mModel->insertRow( mModel->rowCount( QModelIndex() ), rowData, &errorMessage );
|
||||
}
|
||||
result = mModel->insertRow( position, rowData, &errorMessage );
|
||||
|
||||
if ( ! result )
|
||||
{
|
||||
notify( tr( "Error adding row" ), errorMessage, Qgis::MessageLevel::Critical );
|
||||
}
|
||||
else
|
||||
{
|
||||
mRATView->scrollTo( mRATView->model()->index( position, 0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,6 +446,50 @@ void QgsRasterAttributeTableWidget::notify( const QString &title, const QString
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTableWidget::setDelegates()
|
||||
{
|
||||
mClassifyComboBox->clear();
|
||||
if ( mAttributeTable )
|
||||
{
|
||||
const QList<QgsRasterAttributeTable::Field> tableFields { mAttributeTable->fields() };
|
||||
int fieldIdx { 0 };
|
||||
const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
|
||||
for ( const QgsRasterAttributeTable::Field &f : std::as_const( tableFields ) )
|
||||
{
|
||||
// Clear all delegates.
|
||||
mRATView->setItemDelegateForColumn( fieldIdx, nullptr );
|
||||
if ( usageInfo[f.usage].maybeClass )
|
||||
{
|
||||
mClassifyComboBox->addItem( QgsFields::iconForFieldType( f.type ), f.name, QVariant( fieldIdx ) );
|
||||
}
|
||||
fieldIdx++;
|
||||
}
|
||||
|
||||
if ( mAttributeTable->hasColor() )
|
||||
{
|
||||
if ( mAttributeTable->usages().contains( Qgis::RasterAttributeTableFieldUsage::Alpha ) )
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorAlphaDelegate( mRATView ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorDelegate( mRATView ) );
|
||||
}
|
||||
}
|
||||
else if ( mAttributeTable->hasRamp() )
|
||||
{
|
||||
if ( mAttributeTable->usages().contains( Qgis::RasterAttributeTableFieldUsage::AlphaMin ) )
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorRampAlphaDelegate( mRATView ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorRampDelegate( mRATView ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///@cond private
|
||||
|
||||
|
||||
@ -164,6 +164,7 @@ class GUI_EXPORT QgsRasterAttributeTableWidget : public QWidget, private Ui::Qgs
|
||||
void removeRow();
|
||||
void bandChanged( const int index );
|
||||
void notify( const QString &title, const QString &message, Qgis::MessageLevel level = Qgis::MessageLevel::Info );
|
||||
void setDelegates( );
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>336</width>
|
||||
<height>206</height>
|
||||
<width>391</width>
|
||||
<height>222</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -17,7 +17,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="mCreateInfoLabel">
|
||||
<property name="text">
|
||||
<string><p>Create a new Raster Attribute Table (RAT) using the current symbology.</p></string>
|
||||
<string><html><head/><body><p>Create a new Raster Attribute Table (RAT) from the current symbology.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@ -27,23 +27,23 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Raster Attribute Table Destination</string>
|
||||
<string>Raster Attribute Table Storage</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="mNativeRadioButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>This method will save the attribute table using the data provider, </p><p>overwriting any existing attribute table for the raster band used</p><p>by the current style.</p><p><br/></p><p>Depending on the data provider and the raster file format, the </p><p>attribute table will be embedded in the main raster file or saved</p><p> into a sidecar file manged by the data provider.</p><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Data provider</string>
|
||||
<string>Managed by the data provider</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="mDbfRadioButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>This method will save the attribute table into a sidecar VAT.DBF file.</p><p><br/></p><p>The resulting file will not be associated with any particular band.</p></body></html></string>
|
||||
@ -53,8 +53,15 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsFileWidget" name="mDbfPathWidget" native="true"/>
|
||||
<item row="2" column="0">
|
||||
<widget class="QgsFileWidget" name="mDbfPathWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@ -730,9 +730,9 @@ class TestQgsRasterAttributeTable(unittest.TestCase):
|
||||
rat.appendField(QgsRasterAttributeTable.Field('BlueMax', Qgis.RasterAttributeTableFieldUsage.BlueMax, QVariant.Int))
|
||||
|
||||
data_rows = [
|
||||
[2.599999, 2.99999, 'two.5-three', 0, 255, 0, 255, 0, 0],
|
||||
[0, 1.99999, 'zero-two', 255, 0, 0, 0, 255, 0],
|
||||
[2, 2.5, 'two-two.5', 0, 255, 0, 0, 0, 255],
|
||||
[2.50000000001, 3, 'two.5-three', 0, 255, 0, 255, 0, 0],
|
||||
[0, 2, 'zero-two', 255, 0, 0, 0, 255, 0],
|
||||
[2.00000000001, 2.5, 'two-two.5', 0, 255, 0, 0, 0, 255],
|
||||
]
|
||||
|
||||
for row in data_rows:
|
||||
@ -752,7 +752,7 @@ class TestQgsRasterAttributeTable(unittest.TestCase):
|
||||
shader = renderer.shader()
|
||||
|
||||
func = shader.rasterShaderFunction()
|
||||
self.assertEqual([(int(100 * st.offset), st.color.name()) for st in func.sourceColorRamp().stops()], [(66, '#00ff00'), (66, '#00ff00'), (85, '#0000ff'), (85, '#00ff00')]
|
||||
self.assertEqual([(int(100 * st.offset), st.color.name()) for st in func.sourceColorRamp().stops()], [(66, '#00ff00'), (83, '#0000ff')]
|
||||
)
|
||||
|
||||
# Test range classes and discrete colors on float
|
||||
@ -777,6 +777,15 @@ class TestQgsRasterAttributeTable(unittest.TestCase):
|
||||
rat.appendRow(row)
|
||||
|
||||
self.assertTrue(rat.isValid()[0])
|
||||
|
||||
# Test ordered
|
||||
ordered = rat.orderedRows()
|
||||
self.assertEqual(ordered, [
|
||||
[-1e+25, 3000000000000, 'red class', 'class 0', 255, 0, 0],
|
||||
[3000000000000, 1e+20, 'blue class', 'class 1', 0, 0, 255],
|
||||
[1e+20, 5e+25, 'green class', 'class 2', 0, 255, 0],
|
||||
])
|
||||
|
||||
raster.dataProvider().setAttributeTable(1, rat)
|
||||
ok, errors = raster.dataProvider().writeNativeAttributeTable() # spellok
|
||||
self.assertTrue(ok)
|
||||
@ -788,6 +797,23 @@ class TestQgsRasterAttributeTable(unittest.TestCase):
|
||||
func = shader.rasterShaderFunction()
|
||||
self.assertEqual([i[0] for i in renderer.legendSymbologyItems()], ['red class', 'blue class', 'green class'])
|
||||
|
||||
# Test color less athematic RAT
|
||||
rat = raster.attributeTable(1)
|
||||
self.assertTrue(rat.removeField('Red')[0])
|
||||
self.assertTrue(rat.removeField('Green')[0])
|
||||
self.assertTrue(rat.removeField('Blue')[0])
|
||||
self.assertFalse(rat.hasColor())
|
||||
self.assertFalse(rat.hasRamp())
|
||||
|
||||
ramp = rat.colorRamp()
|
||||
ramp, labels = rat.colorRamp()
|
||||
self.assertEqual(len(ramp.stops()) + 1, len(labels))
|
||||
self.assertEqual(labels, ['-1e+25 - 3e+12', '3e+12 - 1e+20', '1e+20 - 5e+25'])
|
||||
|
||||
ramp, labels = rat.colorRamp(2)
|
||||
self.assertEqual(len(ramp.stops()) + 1, len(labels))
|
||||
self.assertEqual(labels, ['red class', 'blue class', 'green class'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user