Skip to content
Permalink
Browse files

Added SLD 1.0 export for rasters

  • Loading branch information
luipir committed Dec 18, 2018
1 parent c3819e8 commit 82e48f9c82ac47ffb2c6f35fb40466aebe1afeb3
Showing with 1,744 additions and 38 deletions.
  1. +2 −2 python/core/auto_generated/qgsmaplayer.sip.in
  2. +11 −0 python/core/auto_generated/raster/qgscontrastenhancement.sip.in
  3. +3 −0 python/core/auto_generated/raster/qgshillshaderenderer.sip.in
  4. +3 −0 python/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in
  5. +3 −0 python/core/auto_generated/raster/qgspalettedrasterrenderer.sip.in
  6. +15 −0 python/core/auto_generated/raster/qgsrasterlayer.sip.in
  7. +7 −0 python/core/auto_generated/raster/qgsrasterrenderer.sip.in
  8. +3 −0 python/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in
  9. +3 −0 python/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in
  10. +27 −5 src/app/qgsrasterlayerproperties.cpp
  11. +7 −0 src/app/qgsrasterlayerproperties.h
  12. +59 −28 src/core/qgsmaplayer.cpp
  13. +2 −2 src/core/qgsmaplayer.h
  14. +48 −0 src/core/raster/qgscontrastenhancement.cpp
  15. +8 −0 src/core/raster/qgscontrastenhancement.h
  16. +78 −0 src/core/raster/qgshillshaderenderer.cpp
  17. +2 −0 src/core/raster/qgshillshaderenderer.h
  18. +119 −0 src/core/raster/qgsmultibandcolorrenderer.cpp
  19. +2 −0 src/core/raster/qgsmultibandcolorrenderer.h
  20. +55 −0 src/core/raster/qgspalettedrasterrenderer.cpp
  21. +2 −0 src/core/raster/qgspalettedrasterrenderer.h
  22. +200 −0 src/core/raster/qgsrasterlayer.cpp
  23. +12 −0 src/core/raster/qgsrasterlayer.h
  24. +16 −0 src/core/raster/qgsrasterrenderer.cpp
  25. +4 −0 src/core/raster/qgsrasterrenderer.h
  26. +151 −0 src/core/raster/qgssinglebandgrayrenderer.cpp
  27. +2 −0 src/core/raster/qgssinglebandgrayrenderer.h
  28. +79 −0 src/core/raster/qgssinglebandpseudocolorrenderer.cpp
  29. +2 −0 src/core/raster/qgssinglebandpseudocolorrenderer.h
  30. +1 −0 tests/src/python/CMakeLists.txt
  31. +249 −1 tests/src/python/test_qgsrasterlayer.py
  32. +569 −0 tests/src/python/test_qgsrasterrerderer_createsld.py
@@ -815,7 +815,7 @@ Export the properties of this layer as named style in a QDomDocument
%End


virtual void exportSldStyle( QDomDocument &doc, QString &errorMsg ) const;
virtual void exportSldStyle( QDomDocument &doc, QString &errorMsg );
%Docstring
Export the properties of this layer as SLD style in a QDomDocument

@@ -856,7 +856,7 @@ record in the users style table in their personal qgis.db)
.. seealso:: :py:func:`saveDefaultStyle`
%End

virtual QString saveSldStyle( const QString &uri, bool &resultFlag ) const;
virtual QString saveSldStyle( const QString &uri, bool &resultFlag );
%Docstring
Saves the properties of this layer to an SLD format file.

@@ -126,6 +126,17 @@ By default it will be generated.

void readXml( const QDomElement &elem );

void toSld( QDomDocument &doc, QDomElement &element ) const;
%Docstring
! Write ContrastEnhancement tags following SLD v1.0 specs
SLD1.0 is limited to the parameters listed in:
https://docs.geoserver.org/stable/en/user/styling/sld/reference/rastersymbolizer.html#contrastenhancement
Btw only <Normalize> + vendor options are supported because there is no clear mapping
of ContrastEnhancement parameters to support <Histogram> or <GammaValue>

.. versionadded:: 3.6
%End

private:
const QgsContrastEnhancement &operator=( const QgsContrastEnhancement & );
};
@@ -57,6 +57,9 @@ Factory method to create a new renderer
virtual QList<int> usesBands() const;


virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;


int band() const;
%Docstring
Returns the band used by the renderer
@@ -68,6 +68,9 @@ Takes ownership
virtual QList<int> usesBands() const;


virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;


private:
QgsMultiBandColorRenderer( const QgsMultiBandColorRenderer & );
const QgsMultiBandColorRenderer &operator=( const QgsMultiBandColorRenderer & );
@@ -93,6 +93,9 @@ Returns the raster band used for rendering the raster.
virtual QList<int> usesBands() const;


virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;


void setSourceColorRamp( QgsColorRamp *ramp /Transfer/ );
%Docstring
Set the source color ``ramp``. Ownership is transferred to the renderer.
@@ -362,6 +362,21 @@ Draws a preview of the rasterlayer into a QImage
virtual QDateTime timestamp() const;


bool writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsStringMap &props = QgsStringMap() );
%Docstring
Writes the symbology of the layer into the document provided in SLD 1.0.0 format

:param node: the node that will have the style element added to it.
:param doc: the document that will have the QDomNode added.
:param errorMessage: reference to string that will be updated with any error messages
:param props: a open ended set of properties that can drive/inform the SLD encoding

:return: true in case of success

.. versionadded:: 3.6
%End


public slots:
void showStatusMessage( const QString &message );

@@ -111,6 +111,13 @@ Returns const reference to origin of min/max values
void setMinMaxOrigin( const QgsRasterMinMaxOrigin &origin );
%Docstring
Sets origin of min/max values
%End

virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;
%Docstring
! Used from subclasses to create SLD Rule elements following SLD v1.0 specs

.. versionadded:: 3.6
%End

protected:
@@ -60,6 +60,9 @@ Takes ownership
virtual QList<int> usesBands() const;


virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;


private:
QgsSingleBandGrayRenderer( const QgsSingleBandGrayRenderer & );
const QgsSingleBandGrayRenderer &operator=( const QgsSingleBandGrayRenderer & );
@@ -82,6 +82,9 @@ Creates a color ramp shader
virtual QList<int> usesBands() const;


virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;


int band() const;
%Docstring
Returns the band used by the renderer
@@ -1863,20 +1863,42 @@ void QgsRasterLayerProperties::saveStyleAs_clicked()
this,
tr( "Save layer properties as style file" ),
lastUsedDir,
tr( "QGIS Layer Style File" ) + " (*.qml)" );
tr( "QGIS Layer Style File" ) + " (*.qml)" + ";;" + tr( "Styled Layer Descriptor" ) + " (*.sld)" );
if ( outputFileName.isEmpty() )
return;

// ensure the user never omits the extension from the file name
if ( !outputFileName.endsWith( QLatin1String( ".qml" ), Qt::CaseInsensitive ) )
outputFileName += QLatin1String( ".qml" );
// set style type depending on extension
StyleType type = StyleType::QML;
if ( outputFileName.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
type = StyleType::SLD;
else
// ensure the user never omits the extension from the file name
if ( !outputFileName.endsWith( QLatin1String( ".qml" ), Qt::CaseInsensitive ) )
outputFileName += QLatin1String( ".qml" );

apply(); // make sure the style to save is uptodate

// then export style
bool defaultLoadedFlag = false;
QString message = mRasterLayer->saveNamedStyle( outputFileName, defaultLoadedFlag );
QString message;
switch ( type )
{
case QML:
{
message = mRasterLayer->saveNamedStyle( outputFileName, defaultLoadedFlag );
break;
}
case SLD:
{
message = mRasterLayer->saveSldStyle( outputFileName, defaultLoadedFlag );
break;
}
}
if ( defaultLoadedFlag )
{
settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( outputFileName ).absolutePath() );
sync();
}
else
QMessageBox::information( this, tr( "Save Style" ), message );
}
@@ -47,6 +47,13 @@ class APP_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private

public:

enum StyleType
{
QML,
SLD
};
Q_ENUM( StyleType )

/**
* \brief Constructor
* \param ml Map layer for which properties will be displayed
@@ -1385,51 +1385,84 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag, Style
return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag, categories );
}

void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg )
{
QDomDocument myDocument = QDomDocument();

QDomNode header = myDocument.createProcessingInstruction( QStringLiteral( "xml" ), QStringLiteral( "version=\"1.0\" encoding=\"UTF-8\"" ) );
myDocument.appendChild( header );

// Create the root element
QDomElement root = myDocument.createElementNS( QStringLiteral( "http://www.opengis.net/sld" ), QStringLiteral( "StyledLayerDescriptor" ) );
root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.1.0" ) );
root.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" ) );
root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
root.setAttribute( QStringLiteral( "xmlns:se" ), QStringLiteral( "http://www.opengis.net/se" ) );
root.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
root.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
myDocument.appendChild( root );

// Create the NamedLayer element
QDomElement namedLayerNode = myDocument.createElement( QStringLiteral( "NamedLayer" ) );
root.appendChild( namedLayerNode );

const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
if ( !vlayer )
QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( this );
if (!vlayer && !rlayer)
{
errorMsg = tr( "Could not save symbology because:\n%1" )
.arg( QStringLiteral( "Non-vector layers not supported yet" ) );
.arg( QStringLiteral( "Non vector or raster layers are supported yet" ) );
return;
}

// Create the root element
QDomElement root = myDocument.createElementNS( QStringLiteral( "http://www.opengis.net/sld" ), QStringLiteral( "StyledLayerDescriptor" ) );
QDomElement layerNode;
if ( vlayer )
{
root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.1.0" ) );
root.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" ) );
root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
root.setAttribute( QStringLiteral( "xmlns:se" ), QStringLiteral( "http://www.opengis.net/se" ) );
root.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
root.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
myDocument.appendChild( root );

// Create the NamedLayer element
layerNode = myDocument.createElement( QStringLiteral( "NamedLayer" ) );
root.appendChild( layerNode );
}

// note rster layer generate only 1.0 SLD version mostly becase seems none is using SE1.1.0 at leasst for rasters
if ( rlayer )
{
// Create the root element
root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0.0" ) );
root.setAttribute( QStringLiteral( "xmlns:gml" ), QStringLiteral( "http://www.opengis.net/gml" ) );
root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
root.setAttribute( QStringLiteral( "xmlns:sld" ), QStringLiteral( "http://www.opengis.net/sld" ) );
myDocument.appendChild( root );

// Create the NamedLayer element
layerNode = myDocument.createElement( QStringLiteral( "UserLayer" ) );
root.appendChild( layerNode );
}

QgsStringMap props;
if ( hasScaleBasedVisibility() )
{
props[ QStringLiteral( "scaleMinDenom" )] = QString::number( mMinScale );
props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( mMaxScale );
props[ QStringLiteral( "scaleMinDenom" ) ] = QString::number( mMinScale );
props[ QStringLiteral( "scaleMaxDenom" ) ] = QString::number( mMaxScale );
}
if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg, props ) )

if (vlayer)
{
errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
return;
if ( !vlayer->writeSld( layerNode, myDocument, errorMsg, props ) )
{
errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
return;
}
}

if (rlayer)
{
if ( !rlayer->writeSld( layerNode, myDocument, errorMsg, props ) )
{
errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
return;
}
}

doc = myDocument;
}

QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag )
{
QString errorMsg;
QDomDocument myDocument;
@@ -1439,22 +1472,20 @@ QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
resultFlag = false;
return errorMsg;
}
const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );

// check if the uri is a file or ends with .sld,
// which indicates that it should become one
QString filename;
if ( vlayer->providerType() == QLatin1String( "ogr" ) )
if ( providerType() == QLatin1String( "ogr" ) )
{
QStringList theURIParts = uri.split( '|' );
filename = theURIParts[0];
}
else if ( vlayer->providerType() == QLatin1String( "gpx" ) )
else if ( providerType() == QLatin1String( "gpx" ) )
{
QStringList theURIParts = uri.split( '?' );
filename = theURIParts[0];
}
else if ( vlayer->providerType() == QLatin1String( "delimitedtext" ) )
else if ( providerType() == QLatin1String( "delimitedtext" ) )
{
filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
// toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
@@ -780,7 +780,7 @@ class CORE_EXPORT QgsMapLayer : public QObject
* \param errorMsg this QString will be initialized on error
* during the execution of writeSymbology
*/
virtual void exportSldStyle( QDomDocument &doc, QString &errorMsg ) const;
virtual void exportSldStyle( QDomDocument &doc, QString &errorMsg );

/**
* Save the properties of this layer as the default style
@@ -818,7 +818,7 @@ class CORE_EXPORT QgsMapLayer : public QObject
* \returns a string with any status or error messages
* \see loadSldStyle()
*/
virtual QString saveSldStyle( const QString &uri, bool &resultFlag ) const;
virtual QString saveSldStyle( const QString &uri, bool &resultFlag );

/**
* Attempts to style the layer using the formatting from an SLD type file.
@@ -377,6 +377,54 @@ void QgsContrastEnhancement::readXml( const QDomElement &elem )
}
}

void QgsContrastEnhancement::toSld( QDomDocument &doc, QDomElement &element ) const
{
if ( doc.isNull() || element.isNull() )
return;

QString algName;
switch( contrastEnhancementAlgorithm() )
{
case StretchToMinimumMaximum:
algName = QStringLiteral( "StretchToMinimumMaximum" );
break;
/* TODO: check if ClipToZero => StretchAndClipToMinimumMaximum
* because value outside min/max ar considered as NoData instead of 0 */
case StretchAndClipToMinimumMaximum:
algName = QStringLiteral( "ClipToMinimumMaximum" );
break;
case ClipToMinimumMaximum:
algName = QStringLiteral( "ClipToMinimumMaximum" );
break;
case NoEnhancement:
return;
default:
QgsDebugMsgLevel( QStringLiteral( "No SLD1.0 convertion yet for stretch algorithm %1" ).arg( contrastEnhancementAlgorithmString( contrastEnhancementAlgorithm() ) ), 4 );
return;
}

// Only <Normalize> is supported
// minValue and maxValue are that values as set depending on "Min /Max value settings"
// parameters
QDomElement normalizeElem = doc.createElement( QStringLiteral( "sld:Normalize" ) );
element.appendChild( normalizeElem );

QDomElement vendorOptionAlgorithmElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
vendorOptionAlgorithmElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "algorithm" ) );
vendorOptionAlgorithmElem.appendChild( doc.createTextNode( algName ) );
normalizeElem.appendChild( vendorOptionAlgorithmElem );

QDomElement vendorOptionMinValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
vendorOptionMinValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "minValue" ) );
vendorOptionMinValueElem.appendChild( doc.createTextNode( QString::number( minimumValue() ) ) );
normalizeElem.appendChild( vendorOptionMinValueElem );

QDomElement vendorOptionMaxValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
vendorOptionMaxValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maxValue" ) );
vendorOptionMaxValueElem.appendChild( doc.createTextNode( QString::number( maximumValue() ) ) );
normalizeElem.appendChild( vendorOptionMaxValueElem );
}

QString QgsContrastEnhancement::contrastEnhancementAlgorithmString( ContrastEnhancementAlgorithm algorithm )
{
switch ( algorithm )

0 comments on commit 82e48f9

Please sign in to comment.
You can’t perform that action at this time.