Skip to content

Expose OGR feature styles via a new "Embedded styling" renderer #209

Description

@nyalldawson

QGIS Enhancement: Expose OGR feature styles via a new "Embedded styling" renderer

Date 2021/02/16

Author Nyall Dawson (@nyalldawson)

Contact nyall dot dawson at gmail dot com

maintainer @nyalldawson

Version QGIS 3.20

Sponsor QGIS Denmark User Group

Summary

Some vector data formats have native support for embedding feature styling information, such as MapInfo TAB files. These formats allow embedded symbology to be set on a feature-by-feature basis. This proposal concerns exposing this per-feature styling information via the QGIS API and creation of a new "embedded styling" renderer for compatible vector layers, which allows users to view features using the closest possible match to their original symbology.

Proposed Solution

This proposal relies on the underlying GDAL "feature styling" support. You can read more about this here: https://gdal.org/user/ogr_feature_style.html

Notably: "The following GDAL vector drivers have varying levels of support for feature styles: DWG (libopencad), DWG (Teigha), DXF, KML (libkml), MapInfo, MicroStation DGN v7 and DGN v8, OpenJUMP JML and PDF."

The main driver behind this work is to expose the existing support in GDAL for MapInfo feature styling, and accordingly no work will be done to enhance the feature styling support for GDAL drivers. It is our hope that the increased exposure of this functionality from GDAL will see more attention (and sponsorship) given to extending the GDAL feature styling functionality and adding support for other suitable formats, to the benefit of all GDAL clients.

API Changes

QgsOgrUtils

A new method will be added to QgsOgrUtils to convert OGR style strings to their equivalent QgsSymbol:

/**
 * Creates a new QgsSymbol matching an OGR style \a string.
 *
 * Caller takes ownership of the returned object.
 */
QgsSymbol* symbolFromStyleString( const QString& string ) SIP_FACTORY;

There is 100% overlap between the symbology supported in OGR style strings and QGIS symbology, so the returned symbols should be a very close match to their OGR representation. (Note that the GDAL reading of symbols from the original data source may be lossy in order to represent them as OGR style strings, so there is no guarantee that the QGIS representation will be an exact match for their original appearance, only that the conversion from GDAL -> QGIS will be lossless.)

QgsFeature

The QgsFeature class will gain a new getter/setter for the feature's embedded symbol:

/**
 * Returns the feature's embedded symbology, or NULLPTR if the feature has no embedded symbol.
 */
const QgsSymbol* embeddedSymbol() const;

/**
 * Sets the feature's embedded \a symbol.
 * Ownership of \a symbol is transferred to the feature.
 */
void setEmbeddedSymbol( QgsSymbol* symbol SIP_TRANSFER );

If present, the symbol will be stored as a unique_ptr in QgsFeaturePrivate. Accordingly the impact on the size of storing QgsFeatures will be minimal, as symbol-less features will only grow by the size of the new pointer member. Furthermore, QgsFeature objects are implicitly shared, so the cost of shallow copies of features with attached symbols will not be impacted.

QgsFeatureRequest

A new QgsFeatureRequest::Flag will be added: EmbeddedSymbols. By default feature requests will NOT retrieve any embedded symbols in order to keep the feature requests as fast as possible. Callers must explicitly opt-in to retrieval and conversion of embedded symbols by setting the new flag on their feature requests.

If the flag is NOT present, then QgsFeatures::embeddedSymbol() will always be nullptr, regardless of the presence or absence of feature level symbology in the underlying dataset.

QgsOgrFeatureIterator

The QgsOgrFeatureIterator class will check for the presence of the QgsFeatureRequest::EmbeddedSymbols flag and if present, the underlying OGR feature styling will be read for each feature and converted to a QgsSymbol set for the returned features.

While this proposal only covers support for OGR feature level symbology, it is entirely possible that feature level symbology is supported by other vector data providers and future work could see their feature iterators gain support for this functionality too. All API will be kept generalised in order to support this potential future work.

QgsVectorDataProvider

QgsVectorDataProvider will gain a new Capability flag FeatureSymbology. Providers which support feature level symbology will return this flag. Initially, only QgsOgrProvider objects reading from supported OGR data sources (i.e. MapInfo TAB files) will return this flag.

Additionally, QgsOgrProvider will also have the QgsVectorDataProvider::createRenderer method implemented so that newly added layers with compatible datasources will use the embedded symbology by default.

QgsFeatureRenderer

QgsFeatureRenderer will gain a new virtual method for determining whether the feature renderer requires embedded symbols:

/**
 * Returns TRUE if the renderer uses embedded symbols for features.
 * The default implementation returns FALSE.
 */
virtual bool usesEmbeddedSymbols() const;

The QgsVectorLayerRenderer class will test the usesEmbeddedSymbols return value for the layer's renderer in order to determine whether the feature requests created by QgsVectorLayerRenderer should include the new QgsFeatureRequest::EmbeddedSymbols flag. By default renderers will indicate that they do not use embedded symbols, so there will be no extra cost to the existing use cases and rendering of layers.

New "Embedded Renderer" vector layer renderer

A new "Embedded Symbol" vector renderer will be created. Whenever this renderer is used, the original embedded symbol for each feature will be rendered. The renderer will also support setting a default fallback symbol to use for features which do not have any embedded symbol available.

Accordingly, users will be able to set their layer to use the Embedded Symbols renderer in order to see the original stored symbology for features!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions