2

I'm trying to write a python function that takes a BGSREF attribute from a BGS geology shapefile and uses it to colour up the geology polygons of a shapefile.

I posted my non-code working as a part answer to Data-defined Styles in QGIS as it shows the maths behind taking a BGSREF code and converting it to RGB values, but I would like to know what needs to be corrected to have the full solution.

Using Nathan's QGIS Blog post on User defined expression functions for QGIS as a basis I created my code like:

from qgis.utils import qgsfunction
from qgis.core import QGis

aColourAdjust = [0,7,14,21,31,42,54,67,80,100]

@qgsfunction(1, "Python")
def BGSrefToQGIScolorRGB(values, feature, parent):
    """
       Converts BGSref colour (attribution) to RGB values.
       Output as integers in the range 0 - 255, in the style:
       color_rgb(rrr,ggg,bbb) or color_rgba(rrr,ggg,bbb,aaa)
    """
    # set some variables
    #rgbValue = "color_rgb(255,255,255)"
    rgbValue = "255,255,255"
    #rgbValue = "<255>,<255>,<255>"
    isTransparent = 'False'

    bgsref2Convert = values[0]

    # Test the length of the input value and pad with zeros if applicable.
    if len(str(bgsref2Convert)) == 1:
        bgsref2Convert = "00" + str(bgsref2Convert)
    elif len(str(bgsref2Convert)) == 2:
        bgsref2Convert = "0" + str(bgsref2Convert)
    elif len(str(bgsref2Convert)) == 3:
        pass
    else:
        isTransparent = 'True'

    #Now we can calculate the RGB values...
    if isTransparent:
        #rgbValue = "color_rgba(255,255,255,255)"
        rgbValue = "255,255,255,255"
        #rgbValue = ""<255>,<255>,<255>,<255>"
    else:
        yellow = aColourAdjust[int(bgsref2Convert[:1])]
        cyan = aColourAdjust[int(bgsref2Convert[-1:])]
        magenta = aColourAdjust[int(bgsref2Convert[1:2])]

        # 100.0 is deliberate to get a floating point result, otherwise the result of division will always be zero.
        red = 255-((cyan/100.0)*255)
        green = 255-((magenta/100.0)*255)
        blue = 255-((yellow/100.0)*255)

        #Now just need to find the expected QGIS format for the colour output...

        #rgbValue = "color_rgb(" + str(int(red)) + "," + str(int(green)) + "," + str(int(blue)) + ")"
        rgbValue = str(int(red)) + "," + str(int(green)) + "," + str(int(blue))
        #rgbValue = "<" + str(int(red)) + ">,<" + str(int(green)) + ">,<" + str(int(blue)) + ">"
    return rgbValue

I start QGIS (2.2.0), open the Python Console, import my function, and it shows in the expression string builder.

For my chosen layer style properties, I choose Single Symbol, data defined properties, Expression and create an expression like: BGSrefToQGIScolorRGB( "BGSREF" ) and apply this, but I never seem to get any colours in the map.

The help tip tells me the colour format is: '<red>,<green>,<blue>,<alpha>' but what does this actually mean?

I've tried outputting the results as the following strings (255 here indicates number format not the literal output):

* <255><255><255><255>
* <255><255><255>
* 255,255,255
* color_rgb(255,255,255)

Nothing seems to work for me.

What format is expected?

mgri
  • 16,159
  • 6
  • 47
  • 80
nmtoken
  • 13,355
  • 5
  • 38
  • 87

2 Answers2

1

You need to set the color of QgsSymbol with .setColor(). setColor() needs a QColor. QColors can be set with three integers for red, green, and blue with values between 0 to 255.

So if you had a symbol "symbol" this would set the color:

symbol.setColor(QColor(red, green, blue))

Edit: You can also set alpha too (QColor(red, green, blue, alpha)), but the default for that is 0.

midfield99
  • 679
  • 7
  • 17
  • Thanks for your quick response. Can you clarify though, do I need to change my python function so that it calls 'symbol.setColor(QColor(red, green, blue))' or is it the expression that I build that needs to use the rgb value that is returned by my function? – nmtoken Mar 10 '14 at 17:19
  • SetColor acts on your symbol. So do it after you initialize the symbol. For example: self.symbol = QgsFillSymbolV2() self.symbol.setColor(QColor(red, green, blue)) – midfield99 Mar 10 '14 at 18:09
  • But I'm not initializing a symbol, or trying to write some standalone pygis that might do this. I trying to write a function that converts an attribute to a colour as part of an expression. The expression builder help tells me that it is expecting a ,,, colour as input, and it's this that I'm trying to do via the function (the output of my function). I may be completely misunderstanding the expression builder here, but I was expecting the expression builder would take the function, apply it to each attribute in the table and symbolize the layer accordingly. – nmtoken Mar 10 '14 at 18:18
  • Ah, I misunderstood that. I thought you were initializing the symbol. I'm not completely sure how you would use python to change the color with an expression builder. ` ',,,' ' just means it expects something like this: 'color_rgb($Value1, $Value2, $Value3)' . 'color_rgb(255,255,255)' should be a valid command in the expression builder though. It works for me. – midfield99 Mar 10 '14 at 18:43
  • It doesn't answer your python problem, but could you get the rgb values computed in excel or open office? .dbf files are spreadsheets, so you could add three columns for red, green and blue values, and then use the expression builder to set the color to 'color_rgb($Field1, $Field2, $Field3)'. Just replace the Fields with the appropriate columns. – midfield99 Mar 10 '14 at 18:47
  • One problem I see is that it looks like your function is returing the string 'color_rgb(255,255,255)'. I think that treats color_rgb as a string, not a function. Try returning values to color_rgb(). So I think it would work better if you set your expression to this: color_rgb(PyFuncCall) if it returned 255,255,255 or color_rgb(call(r),call(b),call(g)) if it returned a single value. I'm not quite sure if qgis would read '255,255,255' as one value or three though. – midfield99 Mar 10 '14 at 19:05
  • I could try other options to get the RGB colours then output them as SLD, or populate them back into the attribute table. The code I'm using above is based on some ColdFusion code I wrote a while back to do the same conversion so I could write out some pdfs; but that kind of defeats the objective. It looks like this should be possible using the data defined properties dialog. – nmtoken Mar 11 '14 at 10:55
  • The code above is outputting a string like '255,255,255'. I tried the other options listed and just now '255,255,255,255' based on this blog posting (http://nathanw.net/2013/06/27/alpha-by-value-choropleth/) which shows (inter al) using the data defined properties for a single symbol, and getting the colour string from the attribute table. But it still isn't working for me. I'll try returning the string to color_rgb() as a single string, otherwise I could try regex to split it up and see where that gets me. – nmtoken Mar 11 '14 at 11:04
  • What does the expression say in the expression based label menu? – midfield99 Mar 11 '14 at 14:08
1

The result of the expression should produce a string like:

255,51,117

or optionally (with an alpha channel value) a string like:

255,255,255,255

nmtoken
  • 13,355
  • 5
  • 38
  • 87
  • With judicious debugging, I realise that I had errors in the python syntax such that the test of isTransparent in the above code ALWAYS evaluates to true, so the conversion never takes place. I have posted the full corrected code to http://gis.stackexchange.com/questions/60450/data-defined-styles-in-qgis/89141#89141. – nmtoken Mar 12 '14 at 12:20