Qgis – Color to layer attribute


Recently I was working with some data layers in QuantumGIS, playing to decide the proper color combination for my visualization. The result had to be presented in a web environment using OpenLayers.

The point is. I define and check the visualization in Qgis, and then I want to export the results to be served as a OpenLayers Vectorlayer, or as a Mapserver layer. Of course, with the color defined in Qgis.

Openlayers and Mapserver give the option to read the color from an attribute value (here and here). Maybe there is an easier way, but in this case I developed a small plugin to export the current visualized color into a specific layer attribute.

Screenshots


..
NOTE from the future:
The plugin has been modified to work for 2.0+ Check out the 2.0 branch on github
..


Plugin Description

The plugin currently supports three basic renderer styles on Qgis. Obtaining only the base color (not the possible patterns).

  • Single symbol
  • Categorized
  • Graduated

Other renderers like rule based, and point displacement will simply throw an error/information message.
Iep, I’m not done with everything. But now works for what I wanted it 🙂 .
V1 renderers will also complain (read the small letters too!).

Code highlights

It all started with a Qgis python-console “script”, but I imagined a plugin and started to drool with the idea. In this section, I will describe some highlights, if you want to read the full code check my github.

In Qgis, each layer is assigned a renderer, and there are various types of renderers that define which symbol to use for each of the features. Given that situation, I created a specific function for every renderer that I was interested in.

def fill_color_attribute_rendererV2(self):
        """ Fill the color attribute using a renderer V2"""

        r = self.renderer
        rtype = type(renderer)
        if rtype == QgsSingleSymbolRendererV2:
            self.fill_color_attribute_singlesymbol_renderer(r)
        elif rtype == QgsCategorizedSymbolRendererV2:
            self.fill_color_attribute_categorizedsymbol_renderer(r)
        elif rtype == QgsGraduatedSymbolRendererV2:
            self.fill_color_attribute_graduatedsymbol_renderer(r)
        else:
            self.fill_color_attribute_custom_renderer(r)

Each one of those functions fills the selected attribute with a color string in hexadecimal. (like “#ff0000”).

Note also that the code in this post is not complete. I omit the process dialog code and similars to leave only the minimum lines needed.

>> Single Symbol

This is the boring one. Get the symbol color and set it everywhere.

  • Get symbol
  • For each feature
    • Assign symbol
def fill_color_attribute_singlesymbol_renderer(self,renderer):
    """ Set the single color into each of the features """
    layer = self.layer
    attribute = self.attribute
    colorstr = str(renderer.symbol().color().name())
    provider = layer.dataProvider()
    feat = QgsFeature()

    newattrs = { attribute : QVariant(colorstr)}

    provider.select(provider.attributeIndexes())

    while provider.nextFeature(feat):
        fid = feat.id()
        provider.changeAttributeValues({ fid : newattrs })

Check github.

>> Categorized

With categorized renderer, a list of categories with symbols is stored, and from this list the features rendered.

  • Get the categorization attribute index
  • for each feature
    • Get the value of the categorization attribute
    • Get the color of that category class
    • Assign the color to the feature

If a category is not found, a default value is set. Fuchsia (“ff00ff”), because I think that nobody will use that for a map (please, don’t).

def fill_color_attribute_categorizedsymbol_renderer(self,renderer):
    """ Set the color with a categorized symbol renderer """
    layer = self.layer
    attribute = self.attribute
    provider = layer.dataProvider()
    attrvalindex = provider.fieldNameIndex(renderer.classAttribute())
    feat = QgsFeature()
    categories = renderer.categories()

    provider.select(provider.attributeIndexes())

    while provider.nextFeature(feat):
        fid = feat.id()
        attribute_map = feat.attributeMap()

        catindex = renderer.categoryIndexForValue(attribute_map[attrvalindex].toString())

        if catindex != -1:
            colorval = categories[catindex].symbol().color().name()
        else:
            colorval = self.NOCOLOR

        newattrs = { attribute : QVariant(colorval)}
        provider.changeAttributeValues({ fid : newattrs })

Check github.

>> Graduated

Finally, the graduated has the same idea as the categorized. But in this case there is no: renderer.categoryIndexForValue(...) to help. So it is necessary to loop the ranges until the correct one (or none) is found.

  • Get the graduation attribute index
  • For each feature
    • Get the value of the graduation attribute
    • For all the renderer symbols (until something found)
      • If the value is in range → store color
    • Assign the color to the feature
def fill_color_attribute_graduatedsymbol_renderer(self,renderer):
    """ Set the color with a graduated symbol renderer """
    layer = self.layer
    attribute = self.attribute
    provider = layer.dataProvider()
    attrvalindex = provider.fieldNameIndex(renderer.classAttribute())
    feat = QgsFeature()

    provider.select(provider.attributeIndexes())

    while provider.nextFeature(feat):
        fid = feat.id()
        attribute_map = feat.attributeMap()
        value = float(attribute_map[attrvalindex].toString())

        colorval = self.NOCOLOR

        for r in renderer.ranges():
            if value >= r.lowerValue() \
                and value <= r.upperValue() \
                and colorval == self.NOCOLOR:
                    colorval = r.symbol().color().name()

        newattrs = { attribute : QVariant(colorval)}
        provider.changeAttributeValues({ fid : newattrs })

Check github.

Final notes

And voilĂ ! The color is stored in an attribute.
If the attribute does not exist, is necessary to create it. And some input sanitizers are implemented (nobody should be able to name an attribute like |\/|1|)i7@).
More renderers exist, I don’t know if I will implement those. Maybe at least the Rule Based one.

References

Qgis ⇒ GO
Qgis API ⇒ GO
pyqgis-cookbook ⇒ GO
QT ⇒ GO
Kxtells Qgis Plugins ⇒ GO

Advertisements

Leave a comment

Filed under code, curious, gis, Maps, tools

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s