[FEATURE] Implement saving paletted raster renderer color tables

Use the .clr/gdal file format, but add the labels on the ends of
the lines. Seems other importers like ArcMap just ignore these.
This commit is contained in:
Nyall Dawson 2017-03-27 12:26:02 +10:00
parent d0566f7792
commit 7eb63d99dc
7 changed files with 106 additions and 7 deletions

View File

@ -39,6 +39,7 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer
static QgsPalettedRasterRenderer::ClassData colorTableToClassData( const QList<QgsColorRampShader::ColorRampItem> &table );
static QgsPalettedRasterRenderer::ClassData classDataFromString( const QString &string );
static QgsPalettedRasterRenderer::ClassData classDataFromFile( const QString &path );
static QString classDataToString( const QgsPalettedRasterRenderer::ClassData &classes );
private:

View File

@ -326,9 +326,11 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromStr
break;
}
case 4:
case 5:
default:
{
if ( lineParts.count() < 4 )
continue;
int value = lineParts.at( 0 ).toInt( &ok );
if ( !ok )
continue;
@ -346,14 +348,20 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromStr
c = QColor( r, g, b );
}
if ( lineParts.count() == 5 )
if ( lineParts.count() >= 5 )
{
double alpha = lineParts.at( 4 ).toDouble( &ok );
if ( ok )
c.setAlpha( alpha );
}
classes << Class( value, c );
QString label;
if ( lineParts.count() > 5 )
{
label = lineParts.mid( 5 ).join( ' ' );
}
classes << Class( value, c, label );
break;
}
}
@ -375,6 +383,24 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromFil
return classDataFromString( input );
}
QString QgsPalettedRasterRenderer::classDataToString( const QgsPalettedRasterRenderer::ClassData &classes )
{
QStringList out;
// must be sorted
QgsPalettedRasterRenderer::ClassData cd = classes;
std::sort( cd.begin(), cd.end(), []( const Class & a, const Class & b ) -> bool
{
return a.value < b.value;
} );
Q_FOREACH ( const Class &c, cd )
{
out << QString( "%1 %2 %3 %4 %5 %6" ).arg( c.value ).arg( c.color.red() )
.arg( c.color.green() ).arg( c.color.blue() ).arg( c.color.alpha() ).arg( c.label );
}
return out.join( '\n' );
}
void QgsPalettedRasterRenderer::updateArrays()
{
// find maximum color index

View File

@ -117,6 +117,7 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
* Converts a \a string containing a color table or class data to to paletted renderer class data.
* @note added in QGIS 3.0
* @see classDataFromFile()
* @see classDataToString()
*/
static QgsPalettedRasterRenderer::ClassData classDataFromString( const QString &string );
@ -127,6 +128,13 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
*/
static QgsPalettedRasterRenderer::ClassData classDataFromFile( const QString &path );
/**
* Converts classes to a string representation, using the .clr/gdal color table file format.
* @note added in QGIS 3.0
* @see classDataFromString()
*/
static QString classDataToString( const QgsPalettedRasterRenderer::ClassData &classes );
private:
int mBand;

View File

@ -92,6 +92,7 @@ QgsPalettedRendererWidget::QgsPalettedRendererWidget( QgsRasterLayer *layer, con
connect( mDeleteEntryButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::deleteEntry );
connect( mAddEntryButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::addEntry );
connect( mLoadFromFileButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::loadColorTable );
connect( mExportToFileButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::saveColorTable );
}
QgsRasterRenderer *QgsPalettedRendererWidget::renderer()
@ -336,6 +337,36 @@ void QgsPalettedRendererWidget::loadColorTable()
}
}
void QgsPalettedRendererWidget::saveColorTable()
{
QgsSettings settings;
QString lastDir = settings.value( QStringLiteral( "lastColorMapDir" ), QDir::homePath() ).toString();
QString fileName = QFileDialog::getSaveFileName( this, tr( "Save file" ), lastDir, tr( "Text (*.clr)" ) );
if ( !fileName.isEmpty() )
{
if ( !fileName.endsWith( QLatin1String( ".clr" ), Qt::CaseInsensitive ) )
{
fileName = fileName + ".clr";
}
QFile outputFile( fileName );
if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
{
QTextStream outputStream( &outputFile );
outputStream << QgsPalettedRasterRenderer::classDataToString( mModel->classData() );
outputStream.flush();
outputFile.close();
QFileInfo fileInfo( fileName );
settings.setValue( QStringLiteral( "lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
}
else
{
QMessageBox::warning( this, tr( "Write access denied" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
}
}
}
//
// QgsPalettedRendererModel
//

View File

@ -108,6 +108,8 @@ class GUI_EXPORT QgsPalettedRendererWidget: public QgsRasterRendererWidget, priv
void changeLabel();
void applyColorRamp();
void loadColorTable();
void saveColorTable();
};
#endif // QGSPALETTEDRENDERERWIDGET_H

View File

@ -480,6 +480,19 @@ class TestQgsRasterLayer(unittest.TestCase):
self.assertEqual(classes[4].color.name(), '#0000ff')
self.assertEqual(classes[4].color.alpha(), 255)
# qgis style, with labels
qgis = '3 255 0 0 255 class 1\n4 0 255 0 200 class 2'
classes = QgsPalettedRasterRenderer.classDataFromString(qgis)
self.assertEqual(len(classes), 2)
self.assertEqual(classes[0].value, 3)
self.assertEqual(classes[0].color.name(), '#ff0000')
self.assertEqual(classes[0].color.alpha(), 255)
self.assertEqual(classes[0].label, 'class 1')
self.assertEqual(classes[1].value, 4)
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[1].color.alpha(), 200)
self.assertEqual(classes[1].label, 'class 2')
# some bad inputs
bad = ''
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
@ -496,9 +509,6 @@ class TestQgsRasterLayer(unittest.TestCase):
bad = '1 255 a 0'
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
self.assertEqual(len(classes), 1)
bad = '1 255 255 0 0 0 0 0 0 0'
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
self.assertEqual(len(classes), 0)
def testLoadPalettedClassDataFromFile(self):
# bad file
@ -533,5 +543,16 @@ class TestQgsRasterLayer(unittest.TestCase):
self.assertEqual(classes[9].value, 10)
self.assertEqual(classes[9].color.name(), '#ffb600')
def testPalettedClassDataToString(self):
classes = [QgsPalettedRasterRenderer.Class(1, QColor(0, 255, 0), 'class 2'),
QgsPalettedRasterRenderer.Class(3, QColor(255, 0, 0), 'class 1')]
self.assertEqual(QgsPalettedRasterRenderer.classDataToString(classes), '1 0 255 0 255 class 2\n3 255 0 0 255 class 1')
# must be sorted by value to work OK in ArcMap
classes = [QgsPalettedRasterRenderer.Class(4, QColor(0, 255, 0), 'class 2'),
QgsPalettedRasterRenderer.Class(3, QColor(255, 0, 0), 'class 1')]
self.assertEqual(QgsPalettedRasterRenderer.classDataToString(classes),
'3 255 0 0 255 class 1\n4 0 255 0 255 class 2')
if __name__ == '__main__':
unittest.main()

10
tests/testdata/raster/test.clr vendored Normal file
View File

@ -0,0 +1,10 @@
1 0 0 0
2 200 200 200
3 0 110 0
4 110 65 0
5 0 0 255
6 0 89 255
7 0 174 255
8 0 255 246
9 238 255 0
10 255 182 0