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!
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:
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:
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::EmbeddedSymbolsflag 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, onlyQgsOgrProviderobjects 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:
The QgsVectorLayerRenderer class will test the
usesEmbeddedSymbolsreturn value for the layer's renderer in order to determine whether the feature requests created by QgsVectorLayerRenderer should include the newQgsFeatureRequest::EmbeddedSymbolsflag. 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!