Skip to content

Commit

Permalink
Getting closer... almost relase-ready
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso authored and nyalldawson committed Nov 8, 2022
1 parent 70c93d4 commit bbb045e
Show file tree
Hide file tree
Showing 26 changed files with 916 additions and 115 deletions.
Expand Up @@ -273,6 +273,10 @@ Appends a row of ``data`` to the RAT, optionally reporting any error in ``errorM
bool writeToFile( const QString &path, QString *errorMessage /Out/ = 0 );
%Docstring
Writes the Raster Attribute Table to a DBF file specified by ``path``, optionally reporting any error in ``errorMessage``, returns ``True`` on success.

.. note::

".vat.dbf" extension is automatically added to the file path if not present.
%End

bool readFromFile( const QString &path, QString *errorMessage /Out/ = 0 );
Expand Down
4 changes: 4 additions & 0 deletions python/gui/auto_generated/qgsfilewidget.sip.in
Expand Up @@ -295,6 +295,10 @@ returns a HTML code with a link to the given file path
Returns a filePath with relative path options applied (or not) !
%End

public:
virtual QSize minimumSizeHint() const;


};


Expand Down
Expand Up @@ -70,9 +70,12 @@ This signal is emitted after a successful classify operation which changed the r
Save the changes in the raster attribute table.
%End

bool setEditable( bool editable );
bool setEditable( bool editable, bool allowCancel = true );
%Docstring
Set the editable state, it may trigger save changes if the attribute table has unsave changes.

:param editable: editable state
:param allowCancel: optional (default ``True``) flag to show cancel option in confirm save dialog.
%End

};
Expand Down
78 changes: 10 additions & 68 deletions src/app/qgisapp.cpp
Expand Up @@ -119,6 +119,7 @@
#include "raster/qgsrasterelevationpropertieswidget.h"
#include "raster/qgsrasterattributetabledialog.h"
#include "raster/qgscreaterasterattributetabledialog.h"
#include "raster/qgsloadrasterattributetabledialog.h"
#include "vector/qgsvectorelevationpropertieswidget.h"
#include "mesh/qgsmeshelevationpropertieswidget.h"
#include "elevation/qgselevationprofilewidget.h"
Expand Down Expand Up @@ -12043,16 +12044,13 @@ void QgisApp::loadRasterAttributeTableFromFile( )
if ( !currentLayer )
return;

QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer );

int bandNumber { 0 };

if ( layer )
if ( QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer ); layer )
{
QgsCreateRasterAttributeTableDialog dlg { layer };
if ( dlg.exec() == QDialog::Accepted )
QgsLoadRasterAttributeTableDialog dlg { layer };
dlg.setMessageBar( visibleMessageBar() );
if ( dlg.exec() == QDialog::Accepted && dlg.openWhenDone() )
{
// TODO!
openRasterAttributeTable();
}
}
}
Expand All @@ -12067,70 +12065,14 @@ void QgisApp::createRasterAttributeTable()
if ( !currentLayer )
return;

QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer );

int bandNumber { 0 };

if ( layer && layer->canCreateRasterAttributeTable() )
if ( QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer ); layer && layer->canCreateRasterAttributeTable() )
{
// Create the attribute table from the renderer and open it
QgsCreateRasterAttributeTableDialog dlg { layer };
if ( dlg.exec() == QDialog::Accepted )
dlg.setMessageBar( visibleMessageBar() );
if ( dlg.exec() == QDialog::Accepted && dlg.openWhenDone() )
{
QString errorMessage;
QgsRasterAttributeTable *rat { QgsRasterAttributeTable::createFromRaster( layer, &bandNumber ) };

if ( ! rat )
{
visibleMessageBar()->pushMessage( tr( "Error Creating Raster Attribute Table" ),
tr( "The raster attribute table could not be created." ),
Qgis::MessageLevel::Critical );
return;
}

layer->dataProvider()->setAttributeTable( bandNumber, rat );

// Save it
const bool saveToFile { dlg.saveToFile() };
if ( saveToFile )
{
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;
}

if ( ! rat->writeToFile( filePath, &errorMessage ) )
{
visibleMessageBar()->pushMessage( tr( "Error Saving Raster Attribute Table" ),
errorMessage,
Qgis::MessageLevel::Critical );
layer->dataProvider()->setAttributeTable( bandNumber, nullptr );
return;
}
}
else
{
if ( ! layer->dataProvider()->writeNativeAttributeTable( &errorMessage ) )
{
visibleMessageBar()->pushMessage( tr( "Error Saving Raster Attribute Table" ),
errorMessage,
Qgis::MessageLevel::Critical );
layer->dataProvider()->setAttributeTable( bandNumber, nullptr );
return;
}

}

visibleMessageBar()->pushMessage( tr( "Raster Attribute Table Saved" ),
tr( "The new Raster Attribute Table was successfully created." ),
Qgis::MessageLevel::Success );

if ( dlg.openWhenDone() )
{
openRasterAttributeTable();
}

openRasterAttributeTable();
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/core/raster/qgsrasterattributetable.cpp
Expand Up @@ -509,6 +509,8 @@ bool QgsRasterAttributeTable::writeToFile( const QString &path, QString *errorMe

writer.reset( QgsVectorFileWriter::create( cleanedPath, qgisFields(), QgsWkbTypes::Type::NoGeometry, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), options ) );

cleanedPath.append( QStringLiteral( ".dbf" ) );

const QgsVectorFileWriter::WriterError error { writer->hasError() };
if ( error != QgsVectorFileWriter::WriterError::NoError )
{
Expand Down Expand Up @@ -1196,7 +1198,7 @@ QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInform
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Name, { tr( "Class Name" ), false, false, false, false, true, true, QList<QVariant::Type>() << QVariant::String } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::MinMax, { tr( "Class Value (min=max)" ), true, true, false, false, true, false, QList<QVariant::Type>() << QVariant::Int << QVariant::LongLong << QVariant::Double } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Min, { tr( "Class Minimum Value" ), true, true, false, false, true, false, QList<QVariant::Type>() << QVariant::Int << QVariant::LongLong << QVariant::Double } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Max, { tr( "Class Maximum Value)" ), true, true, false, false, true, false, QList<QVariant::Type>() << QVariant::Int << QVariant::LongLong << QVariant::Double } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Max, { tr( "Class Maximum Value" ), true, true, false, false, true, false, QList<QVariant::Type>() << QVariant::Int << QVariant::LongLong << QVariant::Double } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Red, { tr( "Red Color Value (0-255)" ), true, false, true, false, true, false, QList<QVariant::Type>() << QVariant::Int } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Green, { tr( "Green Color Value (0-255)" ), true, false, true, false, true, false, QList<QVariant::Type>() << QVariant::Int } );
QgsRasterAttributeTable::sUsageInformation.insert( Qgis::RasterAttributeTableFieldUsage::Blue, { tr( "Blue Color Value (0-255)" ), true, false, true, false, true, false, QList<QVariant::Type>() << QVariant::Int } );
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsrasterattributetable.h
Expand Up @@ -270,6 +270,7 @@ class CORE_EXPORT QgsRasterAttributeTable

/**
* Writes the Raster Attribute Table to a DBF file specified by \a path, optionally reporting any error in \a errorMessage, returns TRUE on success.
* \note ".vat.dbf" extension is automatically added to the file path if not present.
*/
bool writeToFile( const QString &path, QString *errorMessage SIP_OUT = nullptr );

Expand Down
68 changes: 68 additions & 0 deletions src/core/raster/qgsrasterlayer.cpp
Expand Up @@ -1034,6 +1034,69 @@ void QgsRasterLayer::setDataSourcePrivate( const QString &dataSource, const QStr
}
}

void QgsRasterLayer::writeRasterAttributeTableExternalPaths( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
{
if ( attributeTableCount() > 0 )
{
QDomElement elem = doc.createElement( QStringLiteral( "FileBasedAttributeTables" ) );
for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
{
if ( QgsRasterAttributeTable *rat = attributeTable( bandNo ); rat && ! rat->filePath().isEmpty() )
{
QDomElement ratElem = doc.createElement( QStringLiteral( "AttributeTable" ) );
ratElem.setAttribute( QStringLiteral( "band" ), bandNo );
ratElem.setAttribute( QStringLiteral( "path" ), context.pathResolver().writePath( rat->filePath( ) ) );
elem.appendChild( ratElem );
}
}
layerNode.appendChild( elem );
}
}

void QgsRasterLayer::readRasterAttributeTableExternalPaths( const QDomNode &layer_node, QgsReadWriteContext &context ) const
{
const QDomElement ratsElement = layer_node.firstChildElement( QStringLiteral( "FileBasedAttributeTables" ) );
if ( !ratsElement.isNull() && ratsElement.childNodes().count() > 0 )
{
const QDomNodeList ratElements { ratsElement.childNodes() };
for ( int idx = 0; idx < ratElements.count(); idx++ )
{
const QDomNode ratElement { ratElements.at( idx ) };
if ( ratElement.attributes().contains( QStringLiteral( "band" ) )
&& ratElement.attributes().contains( QStringLiteral( "path" ) ) )
{
bool ok;
const int band { ratElement.attributes().namedItem( QStringLiteral( "band" ) ).nodeValue().toInt( &ok ) };

// Check band is ok
if ( ! ok || band <= 0 || band > bandCount() )
{
QgsMessageLog::logMessage( tr( "Error reading raster attribute table: invalid band %1." ).arg( band ), tr( "Raster" ) );
continue;
}

const QString path { context.pathResolver().readPath( ratElement.attributes().namedItem( QStringLiteral( "path" ) ).nodeValue() ) };
if ( ! QFile::exists( path ) )
{
QgsMessageLog::logMessage( tr( "Raster attribute table not found at path: %1." ).arg( path ), tr( "Raster" ) );
continue;
}

std::unique_ptr<QgsRasterAttributeTable> rat = std::make_unique<QgsRasterAttributeTable>();
QString errorMessage;
if ( ! rat->readFromFile( path, &errorMessage ) )
{
QgsMessageLog::logMessage( tr( "Error loading raster attribute table from path %1: %2" ).arg( path, errorMessage ), tr( "Raster" ) );
continue;
}

// All good, set the RAT
mDataProvider->setAttributeTable( band, rat.release() );
}
}
}
}

void QgsRasterLayer::closeDataProvider()
{
setValid( false );
Expand Down Expand Up @@ -2247,6 +2310,8 @@ bool QgsRasterLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &c
}
}

readRasterAttributeTableExternalPaths( layer_node, context );

readStyleManager( layer_node );

return res;
Expand Down Expand Up @@ -2362,6 +2427,9 @@ bool QgsRasterLayer::writeXml( QDomNode &layer_node,
layer_node.appendChild( noData );
}

// Store file-based raster attribute table paths (if any)
writeRasterAttributeTableExternalPaths( layer_node, document, context );

writeStyleManager( layer_node, document );

serverProperties()->writeXml( layer_node, document );
Expand Down
18 changes: 18 additions & 0 deletions src/core/raster/qgsrasterlayer.h
Expand Up @@ -565,6 +565,24 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer, public QgsAbstractProfile
*/
void setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) override;

/**
* Writes the paths to the external raster attribute table files associated with the raster bands.
* \param layerNode layer node
* \param doc document
* \param context read-write context
* \since QGIS 3.30
*/
void writeRasterAttributeTableExternalPaths( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const;

/**
* Reads the paths to the external raster attribute table files associated with the raster bands and loads the raster attribute tables, the raster symbology is not changed.
* \param layerNode layer node
* \param doc document
* \param context read-write context
* \since QGIS 3.30
*/
void readRasterAttributeTableExternalPaths( const QDomNode &layer_node, QgsReadWriteContext &context ) const;

//! \brief Constant defining flag for XML and a constant that signals property not used
const QString QSTRING_NOT_SET;
const QString TRSTRING_NOT_SET;
Expand Down
3 changes: 3 additions & 0 deletions src/gui/CMakeLists.txt
@@ -1,5 +1,6 @@
set(QGIS_GUI_SRCS
raster/qgscreaterasterattributetabledialog.cpp
raster/qgsloadrasterattributetabledialog.cpp
raster/qgsrasterattributetablewidget.cpp
raster/qgsrasterattributetabledialog.cpp
raster/qgsrasterattributetableaddcolumndialog.cpp
Expand Down Expand Up @@ -1286,6 +1287,7 @@ set(QGIS_GUI_HDRS

raster/qgsrasterattributetablewidget.h
raster/qgscreaterasterattributetabledialog.h
raster/qgsloadrasterattributetabledialog.h
raster/qgsrasterattributetabledialog.h
raster/qgsrasterattributetableaddcolumndialog.h
raster/qgsrasterattributetableaddrowdialog.h
Expand Down Expand Up @@ -1455,6 +1457,7 @@ set(QGIS_GUI_UI_HDRS
${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_qgsloadrasterattributetabledialogbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsrasterattributetableaddcolumndialog.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssqlcomposerdialogbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssublayersdialogbase.h
Expand Down
9 changes: 9 additions & 0 deletions src/gui/qgsfilewidget.cpp
Expand Up @@ -432,6 +432,15 @@ QString QgsFileWidget::relativePath( const QString &filePath, bool removeRelativ
return filePath;
}

QSize QgsFileWidget::minimumSizeHint() const
{
QSize size { mLineEdit->minimumSizeHint() };
const QSize btnSize { mFileWidgetButton->minimumSizeHint() };
size.setWidth( size.width() + btnSize.width() );
size.setHeight( std::max( size.height(), btnSize.height() ) );
return size;
}


QString QgsFileWidget::toUrl( const QString &path ) const
{
Expand Down
4 changes: 4 additions & 0 deletions src/gui/qgsfilewidget.h
Expand Up @@ -349,6 +349,10 @@ class GUI_EXPORT QgsFileWidget : public QWidget
//! Returns a filePath with relative path options applied (or not) !
QString relativePath( const QString &filePath, bool removeRelative ) const;

// QWidget interface
public:
QSize minimumSizeHint() const override;

friend class TestQgsFileWidget;
friend class TestQgsExternalStorageFileWidget;
friend class TestQgsExternalResourceWidgetWrapper;
Expand Down
10 changes: 7 additions & 3 deletions src/gui/qgsmaptoolidentify.cpp
Expand Up @@ -1079,6 +1079,9 @@ bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, Qg
valueString = QgsRasterBlock::printValue( value.toDouble() );
}
}
attributes.insert( dprovider->generateBandName( it.key() ), valueString );

// Get raster attribute table attributes
if ( const QgsRasterAttributeTable *rat = layer->attributeTable( it.key() ) )
{
bool ok;
Expand Down Expand Up @@ -1114,13 +1117,14 @@ bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, Qg
default:
ratValue = row.at( colIdx ).toString();
}
derivedAttributes.insert( ratField.name, ratValue );
attributes.insert( ratField.name, ratValue );
}
}
}
}
attributes.insert( dprovider->generateBandName( it.key() ), valueString );
} // end RAT

}

QString label = layer->name();
results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
}
Expand Down

0 comments on commit bbb045e

Please sign in to comment.