Request for comment 3: Symbology (new generation)

Date 24 January 2009
Author Martin Dobias
Contact wonder.sk at gmail dot com
Status accepted

Description

This RFC describes design and implementation of new generation symbology for rendering of vector layers in QGIS. The RFC consists of a brief description of vector rendering capabilities (at time of v1.0), its design, its shortcomings and finally a proposed solution.

Current capabilities

In 1.0 version, users are able to apply the following properties for different feature types:
  • lines - set line width, pen style and color
  • polygons - set fill color and pattern
  • points - use hardcoded symbols (+ set line and fill settings) or svg symbols, set size and/or angle
Symbols are then used by renderers:
  • single symbol - one symbol is used for rendering of all features
  • graduated symbol - given a set of ranges of some numerical attribute, objects are rendered with symbol based on the attribute value
  • continuous color - based on numerical attribute object gets drawn with a symbol with color interpolated between two colors. Actually a special case of graduated symbol
  • unique symbol - given a set of categories from attribute's unique values, objects are rendererd with symbol based on the attribute value
The rendering itself is done in QgsVectorLayer::draw() method. The features are fetched one by one from provider and rendered:
  1. fetch feature
  2. transform from layer coordinates to screen coordinates
  3. renderer sets painter's pen and brush (resp. returns an image when drawing points)
  4. the feature is drawn

Current limitations

This section summarizes limitations of current implementation for production of high-quality maps that need bigger amount of customization, including examples:
  • impossible to combine basic symbols and form complex symbols from them
    • point symbol "star in a circle"
    • line symbol made by thicker and thinner line with different colors
  • impossible to draw lines with a decoration
    • line with an arrow (at the end of line)
    • line made by repeating a marker along it
  • impossible to develop custom rendering styles
    • polygon with numbered vertices
    • point rendered as a combination of icons based on attributes
There are two essential limitations which are solved with the new design:
  • rendered doesn't have full control of drawing, it just sets painter, the drawing itself is done in vector layer
  • symbols can't contain more layers

Proposed design

This section summarizes key concepts of the new symbology implementation.

Symbols and symbol layers

There are three types of symbols: marker symbols (for points), line symbols, fill symbols (for polygons).

Symbols consist of one or more symbol layers.

When rendering a symbol, an appropriate render*() method is called to draw it. Before the first call to render method, symbol has to be initialized by calling startRender() method. Finally, stopRender() method has to be called when rendering is done. All these calls are delegated to symbol layers contained in the symbol.

It is possible to set color of a symbol - this color is set to all symbol layers. Some layers might have the color locked - for them the color is not altered. This is useful when setting color of a multilayer symbol. Similarly, it's possible to set width for line symbols, size and angle for marker symbols

The actual rendering is implemented in symbol layers - classes derived from Qgs***SymbolLayer. These layer types will be available:
  • simple marker - rendering with one of hardcoded markers
  • simple line - usual rendering of a line (with specified width, color and pen style)
  • simple fill - usual rendering of a polygon (with defined fill color, fill pattern and outline)
  • svg marker - rendering with a SVG picture
  • marker line - a line rendered by repeating a marker symbol

Symbol layers are stored in symbol layer registry. More symbol layers can be added in future.

Symbol layers are usually implemented with a function returning a widget (derived from QWidget) for setting symbol layer properties. Symbol layers that won't implement it won't be customizable from GUI.

Symbol layer properties (e.g. color, pen width) are specified as key-value pairs of strings. The properties are used when creating and saving symbol layers.

Color ramps

Color ramps are used for defining a range of colors that can be used during creation of renderers - symbol's color will be set from the color ramp.

There are two types of color ramps:
  • gradient - linear gradient from one color to some other
  • random - randomly generated colors from a specified area of color space

Styles

Style groups a set of various symbols and color ramps (in future more items can be added, e.g. font setting).

User can define his own prefered / frequently used symbols, then he will be able to use them without having to recreate them everytime.

Style items (symbols and color ramps) have always a name by which they can be queried from the style.

There will be one default style in QGIS (modifiable), user will have the possibility to add new styles.

Styles are stored in an XML file with well-defined structure. This is an example of line symbol composed from two layers:

<symbol type="line" name="bigway">
    <layer class="SimpleLine" locked="1">
        <prop k="color" v="0,0,0" />
        <prop k="width" v="3" />
        <prop k="penstyle" v="solid" />
    </layer>
    <layer class="SimpleLine" locked="0">
        <prop k="color" v="255,0,0" />
        <prop k="width" v="1" />
        <prop k="penstyle" v="solid" />
    </layer>
</symbol>

Renderers

Renderer is responsible for drawing a feature with correct symbol. There are three types:
  • single symbol
  • categorized (called unique color before)
  • graduated

There is no continuous color renderer because it is in fact only a special case of graduated renderer.

Categorized and graduated renderer can be created by specifying a symbol and color ramp - they will set colors for symbols appropriately.

Proposed user interface

Renderer settings dialog (will be embedded in vector layer properties, in symbology tab)

Symbol selector dialog - shown in case that user wants to change symbol. It shows symbols available in style and allows user to change symbol's properties.

Symbol properties dialog - allows to add and delete symbol layers, change their layer types and properties.

Color ramp dialogs - gradient and random colors

Style manager - overview of symbols and color ramps available in a style, allows to add and remove items

Implementation

Python plugin

The described proposal has been implemented as a python plugin, you can get it from my repository:

http://mapserver.sk/~wonder/qgis/plugins-sandbox.xml

QGIS integration

Because of backward compatibility in 1.x series it's not possible to replace original classes with new ones with different interfaces and semantics. New implementation will exist side by side with original implementation. New classes will have a suffix (or prefix) added for better distinction, e.g. QgsSymbolV2. In version 2.0 old classes can be removed completely and the new ones will be renamed.

The work can be divided into three phases:
  1. implementation of core classes and connection with QGIS infrastructure:
  2. * QgsVectorLayer - new methods drawV2(), rendererV2() and setRendererV2() will be added
  3. * QgsMapRenderer - new method setRenderingVersion() will be added - it will
  4. implementation of dialogs
  5. migration of QGIS application from original to new implementation

Comments

  • Tim Sutton: I think one important addition will be the ability to use true type font based markers as used by other GIS applications. As mentioned in the mailing lists, it would also be good to try to keep the way open for setting styles from OGC Styled Layer Descriptor documents, and exporting styles to SLD docs (although its not necessary to implement this from the beginning). Some other things that would be good to consider: transparency per style layer, allowing different offsets per style layer so that e.g. a polygon can be rendered with an outline and then a broader outline that falls only inside the polygon (often used when creating maps with politcal boundaries for example). It would also be nice to be able to specify a blur factor for style layers so that 'soft' lines can be created and effects such as drop shadows. BR I noticed that rendering in the python prototype is quite slow which I guess is a factor of a) using an interpreted lang for the renderers and b) the extra work that needs to be done when e.g. drawing symbol lines. I assume the C++ implementation will remediate this somewhat, but it may also be a good idea to implement this in tandem with the composition engine that we have oft discussed.
    SLD import/export might be good addition later (although personally I don't see much use for it now). The design allows relatively simple addition of more options you mention - like transparency or offsets. The prototype is slow indeed, there's quite some overhead with Python-C++ conversions and some optimizations like caching of markers are missing. Composition engine can be done completely separately because it would operate with whole rendered layers. --Martin

Voting History

Your Name Vote Comments (optional)
Tim Sutton +1
Marco Hugentobler +1
John C. Tull +1
Gary Sherman +1

Further reading

Symbology-uml-symbol.png (55.2 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-uml-colorramp.png (11.6 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-uml-renderer.png (14.9 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-ng2.png (18.5 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-ng3.png (15.3 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-ng4.png (26.9 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-colorramp-gradient.png (14.3 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-colorramp-random.png (18.1 KB) Redmine Admin, 2011-11-23 04:44 PM

Symbology-style.png (16.6 KB) Redmine Admin, 2011-11-23 04:44 PM