Skip to content

Commit

Permalink
Add method to read paletted color data from string
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Apr 3, 2017
1 parent 93cfde0 commit a6d3af7
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 19 deletions.
1 change: 1 addition & 0 deletions python/core/raster/qgspalettedrasterrenderer.sip
Expand Up @@ -37,6 +37,7 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer
void setSourceColorRamp( QgsColorRamp *ramp /Transfer/ );
QgsColorRamp *sourceColorRamp() const;
static QgsPalettedRasterRenderer::ClassData colorTableToClassData( const QList<QgsColorRampShader::ColorRampItem> &table );
static QgsPalettedRasterRenderer::ClassData classDataFromString( const QString &string );

private:

Expand Down
71 changes: 71 additions & 0 deletions src/core/raster/qgspalettedrasterrenderer.cpp
Expand Up @@ -291,6 +291,77 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::colorTableToClas
return classes;
}

QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromString( const QString &string )
{
QgsPalettedRasterRenderer::ClassData classes;

QRegularExpression linePartRx( "[\\s,:]+" );

QStringList parts = string.split( '\n', QString::SkipEmptyParts );
Q_FOREACH ( const QString &part, parts )
{
QStringList lineParts = part.split( linePartRx, QString::SkipEmptyParts );
bool ok = false;
switch ( lineParts.count() )
{
case 1:
{
int value = lineParts.at( 0 ).toInt( &ok );
if ( !ok )
continue;

classes << Class( value );
break;
}

case 2:
{
int value = lineParts.at( 0 ).toInt( &ok );
if ( !ok )
continue;

QColor c( lineParts.at( 1 ) );

classes << Class( value, c );
break;
}

case 4:
case 5:
{
int value = lineParts.at( 0 ).toInt( &ok );
if ( !ok )
continue;

bool rOk = false;
double r = lineParts.at( 1 ).toDouble( &rOk );
bool gOk = false;
double g = lineParts.at( 2 ).toDouble( &gOk );
bool bOk = false;
double b = lineParts.at( 3 ).toDouble( &bOk );

QColor c;
if ( rOk && gOk && bOk )
{
c = QColor( r, g, b );
}

if ( lineParts.count() == 5 )
{
double alpha = lineParts.at( 4 ).toDouble( &ok );
if ( ok )
c.setAlpha( alpha );
}

classes << Class( value, c );
break;
}
}

}
return classes;
}

void QgsPalettedRasterRenderer::updateArrays()
{
// find maximum color index
Expand Down
6 changes: 6 additions & 0 deletions src/core/raster/qgspalettedrasterrenderer.h
Expand Up @@ -113,6 +113,12 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
*/
static QgsPalettedRasterRenderer::ClassData colorTableToClassData( const QList<QgsColorRampShader::ColorRampItem> &table );

/**
* Converts a \a string containing a color table or class data to to paletted renderer class data.
* @note added in QGIS 3.0
*/
static QgsPalettedRasterRenderer::ClassData classDataFromString( const QString &string );

private:

int mBand;
Expand Down
151 changes: 132 additions & 19 deletions tests/src/python/test_qgsrasterlayer.py
Expand Up @@ -299,8 +299,8 @@ def testPaletted(self):
self.assertTrue(layer.isValid(), 'Raster not loaded: {}'.format(path))

renderer = QgsPalettedRasterRenderer(layer.dataProvider(), 1,
{3: QgsPalettedRasterRenderer.Class(QColor(255, 0, 0), 'class 1'),
1: QgsPalettedRasterRenderer.Class(QColor(0, 255, 0), 'class 2')})
[QgsPalettedRasterRenderer.Class(1, QColor(0, 255, 0), 'class 2'),
QgsPalettedRasterRenderer.Class(3, QColor(255, 0, 0), 'class 1')])

self.assertEqual(renderer.nColors(), 2)
self.assertEqual(renderer.usesBands(), [1])
Expand All @@ -319,10 +319,12 @@ def testPaletted(self):

# test retrieving classes
classes = renderer.classes()
self.assertEqual(classes[1].label, 'class 2')
self.assertEqual(classes[3].label, 'class 1')
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[3].color.name(), '#ff0000')
self.assertEqual(classes[0].value, 1)
self.assertEqual(classes[1].value, 3)
self.assertEqual(classes[0].label, 'class 2')
self.assertEqual(classes[1].label, 'class 1')
self.assertEqual(classes[0].color.name(), '#00ff00')
self.assertEqual(classes[1].color.name(), '#ff0000')

# test set label
# bad index
Expand All @@ -339,10 +341,12 @@ def testPaletted(self):
# clone
new_renderer = renderer.clone()
classes = new_renderer.classes()
self.assertEqual(classes[1].label, 'class 2')
self.assertEqual(classes[3].label, 'new class')
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[3].color.name(), '#ff0000')
self.assertEqual(classes[0].value, 1)
self.assertEqual(classes[1].value, 3)
self.assertEqual(classes[0].label, 'class 2')
self.assertEqual(classes[1].label, 'new class')
self.assertEqual(classes[0].color.name(), '#00ff00')
self.assertEqual(classes[1].color.name(), '#ff0000')
self.assertEqual(new_renderer.sourceColorRamp().type(), 'random')
self.assertEqual(new_renderer.sourceColorRamp().count(), 5)

Expand All @@ -355,10 +359,12 @@ def testPaletted(self):
self.assertEqual(restored.usesBands(), [1])
classes = restored.classes()
self.assertTrue(classes)
self.assertEqual(classes[1].label, 'class 2')
self.assertEqual(classes[3].label, 'new class')
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[3].color.name(), '#ff0000')
self.assertEqual(classes[0].value, 1)
self.assertEqual(classes[1].value, 3)
self.assertEqual(classes[0].label, 'class 2')
self.assertEqual(classes[1].label, 'new class')
self.assertEqual(classes[0].color.name(), '#00ff00')
self.assertEqual(classes[1].color.name(), '#ff0000')
self.assertEqual(restored.sourceColorRamp().type(), 'random')
self.assertEqual(restored.sourceColorRamp().count(), 5)

Expand All @@ -380,12 +386,119 @@ def testPalettedColorTableToClassData(self):
QgsColorRampShader.ColorRampItem(6, QColor(0, 0, 255), 'item3'),
]
classes = QgsPalettedRasterRenderer.colorTableToClassData(entries)
self.assertEqual(classes[5].label, 'item1')
self.assertEqual(classes[3].label, 'item2')
self.assertEqual(classes[6].label, 'item3')
self.assertEqual(classes[5].color.name(), '#ff0000')
self.assertEqual(classes[0].value, 5)
self.assertEqual(classes[1].value, 3)
self.assertEqual(classes[2].value, 6)
self.assertEqual(classes[0].label, 'item1')
self.assertEqual(classes[1].label, 'item2')
self.assertEqual(classes[2].label, 'item3')
self.assertEqual(classes[0].color.name(), '#ff0000')
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[2].color.name(), '#0000ff')

def testLoadPalettedColorDataFromString(self):
"""
Test interpreting a bunch of color data format strings
"""
esri_clr_format = '1 255 255 0\n2 64 0 128\n3 255 32 32\n4 0 255 0\n5 0 0 255'
esri_clr_format_win = '1 255 255 0\r\n2 64 0 128\r\n3 255 32 32\r\n4 0 255 0\r\n5 0 0 255'
esri_clr_format_tab = '1\t255\t255\t0\n2\t64\t0\t128\n3\t255\t32\t32\n4\t0\t255\t0\n5\t0\t0\t255'
esri_clr_spaces = '1 255 255 0\n2 64 0 128\n3 255 32 32\n4 0 255 0\n5 0 0 255'
gdal_clr_comma = '1,255,255,0\n2,64,0,128\n3,255,32,32\n4,0,255,0\n5,0,0,255'
gdal_clr_colon = '1:255:255:0\n2:64:0:128\n3:255:32:32\n4:0:255:0\n5:0:0:255'
for f in [esri_clr_format,
esri_clr_format_win,
esri_clr_format_tab,
esri_clr_spaces,
gdal_clr_comma,
gdal_clr_colon]:
classes = QgsPalettedRasterRenderer.classDataFromString(f)
self.assertEqual(len(classes), 5)
self.assertEqual(classes[0].value, 1)
self.assertEqual(classes[0].color.name(), '#ffff00')
self.assertEqual(classes[1].value, 2)
self.assertEqual(classes[1].color.name(), '#400080')
self.assertEqual(classes[2].value, 3)
self.assertEqual(classes[2].color.name(), '#ff2020')
self.assertEqual(classes[3].value, 4)
self.assertEqual(classes[3].color.name(), '#00ff00')
self.assertEqual(classes[4].value, 5)
self.assertEqual(classes[4].color.name(), '#0000ff')

grass_named_colors = '0 white\n1 yellow\n3 black\n6 blue\n9 magenta\n11 aqua\n13 grey\n14 gray\n15 orange\n19 brown\n21 purple\n22 violet\n24 indigo\n90 green\n180 cyan\n270 red\n'
classes = QgsPalettedRasterRenderer.classDataFromString(grass_named_colors)
self.assertEqual(len(classes), 16)
self.assertEqual(classes[0].value, 0)
self.assertEqual(classes[0].color.name(), '#ffffff')
self.assertEqual(classes[1].value, 1)
self.assertEqual(classes[1].color.name(), '#ffff00')
self.assertEqual(classes[2].value, 3)
self.assertEqual(classes[2].color.name(), '#000000')
self.assertEqual(classes[3].value, 6)
self.assertEqual(classes[3].color.name(), '#0000ff')
self.assertEqual(classes[4].value, 9)
self.assertEqual(classes[4].color.name(), '#ff00ff')
self.assertEqual(classes[5].value, 11)
self.assertEqual(classes[5].color.name(), '#00ffff')
self.assertEqual(classes[6].value, 13)
self.assertEqual(classes[6].color.name(), '#808080')
self.assertEqual(classes[7].value, 14)
self.assertEqual(classes[7].color.name(), '#808080')
self.assertEqual(classes[8].value, 15)
self.assertEqual(classes[8].color.name(), '#ffa500')
self.assertEqual(classes[9].value, 19)
self.assertEqual(classes[9].color.name(), '#a52a2a')
self.assertEqual(classes[10].value, 21)
self.assertEqual(classes[10].color.name(), '#800080')
self.assertEqual(classes[11].value, 22)
self.assertEqual(classes[11].color.name(), '#ee82ee')
self.assertEqual(classes[12].value, 24)
self.assertEqual(classes[12].color.name(), '#4b0082')
self.assertEqual(classes[13].value, 90)
self.assertEqual(classes[13].color.name(), '#008000')
self.assertEqual(classes[14].value, 180)
self.assertEqual(classes[14].color.name(), '#00ffff')
self.assertEqual(classes[15].value, 270)
self.assertEqual(classes[15].color.name(), '#ff0000')

gdal_alpha = '1:255:255:0:0\n2:64:0:128:50\n3:255:32:32:122\n4:0:255:0:200\n5:0:0:255:255'
classes = QgsPalettedRasterRenderer.classDataFromString(gdal_alpha)
self.assertEqual(len(classes), 5)
self.assertEqual(classes[0].value, 1)
self.assertEqual(classes[0].color.name(), '#ffff00')
self.assertEqual(classes[0].color.alpha(), 0)
self.assertEqual(classes[1].value, 2)
self.assertEqual(classes[1].color.name(), '#400080')
self.assertEqual(classes[1].color.alpha(), 50)
self.assertEqual(classes[2].value, 3)
self.assertEqual(classes[2].color.name(), '#ff2020')
self.assertEqual(classes[2].color.alpha(), 122)
self.assertEqual(classes[3].value, 4)
self.assertEqual(classes[3].color.name(), '#00ff00')
self.assertEqual(classes[6].color.name(), '#0000ff')
self.assertEqual(classes[3].color.alpha(), 200)
self.assertEqual(classes[4].value, 5)
self.assertEqual(classes[4].color.name(), '#0000ff')
self.assertEqual(classes[4].color.alpha(), 255)

# some bad inputs
bad = ''
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
self.assertEqual(len(classes), 0)
bad = '\n\n\n'
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
self.assertEqual(len(classes), 0)
bad = 'x x x x'
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
self.assertEqual(len(classes), 0)
bad = '1 255 0 0\n2 255 255\n3 255 0 255'
classes = QgsPalettedRasterRenderer.classDataFromString(bad)
self.assertEqual(len(classes), 2)
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)


if __name__ == '__main__':
Expand Down

0 comments on commit a6d3af7

Please sign in to comment.