Skip to content

Commit

Permalink
Merge pull request #43927 from mhugent/raster_legend_placeholder_icon
Browse files Browse the repository at this point in the history
Feature: possibility to use placeholder icon in legend for raster layer. funded by [Canton of Glarus] [https://www.gl.ch/verwaltung/bau-und-umwelt/hochbau/raumentwicklung-und-geoinformation/geoportal-kanton-glarus.html/808]
  • Loading branch information
mhugent committed Jul 3, 2021
2 parents ca3812a + b11df93 commit ec374d5
Show file tree
Hide file tree
Showing 13 changed files with 3,406 additions and 31 deletions.
18 changes: 18 additions & 0 deletions python/core/auto_generated/qgsmaplayer.sip.in
Expand Up @@ -1429,6 +1429,24 @@ Returns the layer's temporal properties. This may be ``None``, depending on the
Returns the layer's elevation properties. This may be ``None``, depending on the layer type.

.. versionadded:: 3.18
%End

QString legendPlaceholderImage() const;
%Docstring
Returns path to the placeholder image or an empty string if a generated legend is shown

:return: placholder image path

.. versionadded:: 3.22
%End

void setLegendPlaceholderImage( const QString &imgPath );
%Docstring
Set placeholder image for legend. If the string is empty, a generated legend will be shown.

:param imgPath: file path to the placeholder image

.. versionadded:: 3.22
%End

public slots:
Expand Down
14 changes: 9 additions & 5 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Expand Up @@ -946,19 +946,23 @@ QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemCo
{
Q_UNUSED( itemHeight )

if ( ctx && ctx->painter )
if ( ctx && ctx->painter && ctx->context )
{
QgsScopedRenderContextScaleToPixels scopedScaleToPixels( *( ctx->context ) );
double scaleFactor = ctx->context->scaleFactor();
double imgWidth = settings.wmsLegendSize().width() * scaleFactor;
double imgHeight = settings.wmsLegendSize().height() * scaleFactor;

QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
switch ( settings.symbolAlignment() )
{
case Qt::AlignLeft:
default:
ctx->painter->drawImage( QRectF( ctx->columnLeft, ctx->top, settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) );
ctx->painter->drawImage( QPointF( ctx->columnLeft * scaleFactor, ctx->top * scaleFactor ), scaledImg );
break;

case Qt::AlignRight:
ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(), ctx->top, settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) );
ctx->painter->drawImage( QPointF( ctx->columnRight * scaleFactor - imgWidth, ctx->top * scaleFactor ), scaledImg );
break;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/core/qgsmaplayer.cpp
Expand Up @@ -406,6 +406,8 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteCon
mWgs84Extent = QgsXmlUtils::readRectangle( wgs84ExtentNode.toElement() );
}

mLegendPlaceholderImage = layerElement.attribute( QStringLiteral( "legendPlaceholderImage" ) );

return ! layerError;
} // bool QgsMapLayer::readLayerXML

Expand Down Expand Up @@ -576,6 +578,8 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume
mMetadata.writeMetadataXml( myMetadataElem, document );
layerElement.appendChild( myMetadataElem );

layerElement.setAttribute( QStringLiteral( "legendPlaceholderImage" ), mLegendPlaceholderImage );

// now append layer node to map layer node
return writeXml( layerElement, document, context );
}
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsmaplayer.h
Expand Up @@ -1279,6 +1279,20 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
virtual QgsMapLayerElevationProperties *elevationProperties() { return nullptr; }

/**
* Returns path to the placeholder image or an empty string if a generated legend is shown
* \return placholder image path
* \since QGIS 3.22
*/
QString legendPlaceholderImage() const { return mLegendPlaceholderImage;}

/**
* Set placeholder image for legend. If the string is empty, a generated legend will be shown.
* \param imgPath file path to the placeholder image
* \since QGIS 3.22
*/
void setLegendPlaceholderImage( const QString &imgPath ) { mLegendPlaceholderImage = imgPath; }

public slots:

/**
Expand Down Expand Up @@ -1867,6 +1881,9 @@ class CORE_EXPORT QgsMapLayer : public QObject
//! To avoid firing multiple time repaintRequested signal on circular layer circular dependencies
bool mRepaintRequestedFired = false;

//! Path to placeholder image for layer legend. If the string is empty, a generated legend is shown
QString mLegendPlaceholderImage;

friend class QgsVectorLayer;
};

Expand Down
24 changes: 22 additions & 2 deletions src/core/qgsmaplayerlegend.cpp
Expand Up @@ -14,7 +14,8 @@
***************************************************************************/

#include "qgsmaplayerlegend.h"

#include "qgsiconutils.h"
#include "qgsimagecache.h"
#include "qgssettings.h"
#include "qgslayertree.h"
#include "qgslayertreemodellegendnode.h"
Expand Down Expand Up @@ -359,6 +360,18 @@ QList<QgsLayerTreeModelLegendNode *> QgsDefaultVectorLayerLegend::createLayerTre
{
QList<QgsLayerTreeModelLegendNode *> nodes;

if ( mLayer )
{
QString placeholderImage = mLayer->legendPlaceholderImage();
if ( !placeholderImage.isEmpty() )
{
bool fitsInCache;
QImage img = QgsApplication::imageCache()->pathAsImage( placeholderImage, QSize(), false, 1.0, fitsInCache );
nodes << new QgsImageLegendNode( nodeLayer, img );
return nodes;
}
}

QgsFeatureRenderer *r = mLayer->renderer();
if ( !r )
return nodes;
Expand Down Expand Up @@ -502,7 +515,14 @@ QList<QgsLayerTreeModelLegendNode *> QgsDefaultRasterLayerLegend::createLayerTre
nodes << new QgsWmsLegendNode( nodeLayer );
}

if ( mLayer->renderer() )
QString placeholderImage = mLayer->legendPlaceholderImage();
if ( !placeholderImage.isEmpty() )
{
bool fitsInCache;
QImage img = QgsApplication::imageCache()->pathAsImage( placeholderImage, QSize(), false, 1.0, fitsInCache );
nodes << new QgsImageLegendNode( nodeLayer, img );
}
else if ( mLayer->renderer() )
nodes.append( mLayer->renderer()->createLegendNodes( nodeLayer ) );
return nodes;
}
Expand Down
3 changes: 3 additions & 0 deletions src/gui/raster/qgsrasterlayerproperties.cpp
Expand Up @@ -914,6 +914,8 @@ void QgsRasterLayerProperties::sync()
QVariant wmsBackgroundLayer = mRasterLayer->customProperty( QStringLiteral( "WMSBackgroundLayer" ), false );
mBackgroundLayerCheckBox->setChecked( wmsBackgroundLayer.toBool() );

mLegendPlaceholderWidget->setLastPathSettingsKey( QStringLiteral( "lastLegendPlaceholderDir" ) );
mLegendPlaceholderWidget->setSource( mRasterLayer->legendPlaceholderImage() );
mLegendConfigEmbeddedWidget->setLayer( mRasterLayer );

mTemporalWidget->syncToLayer();
Expand Down Expand Up @@ -946,6 +948,7 @@ void QgsRasterLayerProperties::apply()
/*
* Legend Tab
*/
mRasterLayer->setLegendPlaceholderImage( mLegendPlaceholderWidget->source() );
mLegendConfigEmbeddedWidget->applyToLayer();

QgsDebugMsgLevel( QStringLiteral( "apply processing symbology tab" ), 3 );
Expand Down
17 changes: 16 additions & 1 deletion src/gui/vector/qgsvectorlayerlegendwidget.cpp
Expand Up @@ -21,6 +21,7 @@
#include <QTreeWidget>

#include "qgsexpressionbuilderdialog.h"
#include "qgsfilecontentsourcelineedit.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerlegend.h"
#include "qgsrenderer.h"
Expand Down Expand Up @@ -71,10 +72,19 @@ QgsVectorLayerLegendWidget::QgsVectorLayerLegendWidget( QWidget *parent )
labelLegendLayout->addWidget( mLabelLegendTreeWidget );
mLabelLegendGroupBox->setLayout( labelLegendLayout );

mPlaceholderImageLabel = new QLabel( tr( "Legend placeholder image" ) );
mImageSourceLineEdit = new QgsImageSourceLineEdit();
mImageSourceLineEdit->setLastPathSettingsKey( QStringLiteral( "lastLegendPlaceholderDir" ) );
if ( mLayer )
{
mImageSourceLineEdit->setSource( mLayer->legendPlaceholderImage() );
}


QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins( 0, 0, 0, 0 );
layout->addWidget( mPlaceholderImageLabel );
layout->addWidget( mImageSourceLineEdit );
layout->addWidget( mShowLabelLegendCheckBox );
layout->addWidget( mLabelLegendGroupBox );
layout->addWidget( mTextOnSymbolGroupBox );
Expand Down Expand Up @@ -122,7 +132,10 @@ void QgsVectorLayerLegendWidget::setLayer( QgsVectorLayer *layer )
mTextOnSymbolGroupBox->setChecked( legend->textOnSymbolEnabled() );
mTextOnSymbolFormatButton->setTextFormat( legend->textOnSymbolTextFormat() );
populateLegendTreeView( legend->textOnSymbolContent() );

if ( mLayer )
{
mImageSourceLineEdit->setSource( mLayer->legendPlaceholderImage() );
}
}

void QgsVectorLayerLegendWidget::populateLabelLegendTreeWidget()
Expand Down Expand Up @@ -221,6 +234,8 @@ void QgsVectorLayerLegendWidget::applyToLayer()
applyLabelLegend();
}

mLayer->setLegendPlaceholderImage( mImageSourceLineEdit->source() );

mLayer->setLegend( legend );
}

Expand Down
3 changes: 3 additions & 0 deletions src/gui/vector/qgsvectorlayerlegendwidget.h
Expand Up @@ -25,6 +25,7 @@
#include "qgis_gui.h"

class QCheckBox;
class QgsImageSourceLineEdit;
class QLabel;
class QPushButton;
class QTreeView;
Expand Down Expand Up @@ -76,6 +77,8 @@ class GUI_EXPORT QgsVectorLayerLegendWidget : public QWidget
QCheckBox *mShowLabelLegendCheckBox = nullptr;
QgsCollapsibleGroupBox *mLabelLegendGroupBox = nullptr;
QTreeWidget *mLabelLegendTreeWidget = nullptr;
QLabel *mPlaceholderImageLabel = nullptr;
QgsImageSourceLineEdit *mImageSourceLineEdit = nullptr;

QgsMapCanvas *mCanvas = nullptr;
QgsVectorLayer *mLayer = nullptr;
Expand Down
67 changes: 44 additions & 23 deletions src/ui/qgsrasterlayerpropertiesbase.ui
Expand Up @@ -254,7 +254,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="mOptsPage_Information">
<layout class="QVBoxLayout" name="verticalLayout_20">
Expand Down Expand Up @@ -299,8 +299,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>643</width>
<height>679</height>
<width>650</width>
<height>673</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
Expand Down Expand Up @@ -1563,8 +1563,8 @@ border-radius: 2px;</string>
<rect>
<x>0</x>
<y>0</y>
<width>577</width>
<height>190</height>
<width>650</width>
<height>673</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
Expand Down Expand Up @@ -1633,8 +1633,8 @@ border-radius: 2px;</string>
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Fira Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Cantarell';&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Cantarell'; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -1739,21 +1739,15 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
<widget class="QWidget" name="mOptsPage_Legend">
<layout class="QVBoxLayout" name="verticalLayout_18">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_15">
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Embedded Widgets in Legend</string>
</property>
Expand All @@ -1764,6 +1758,16 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QgsImageSourceLineEdit" name="mLegendPlaceholderWidget" native="true"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="mLegendPlaceholderLabel">
<property name="text">
<string>Legend placeholder image</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="mOptsPage_Server">
Expand Down Expand Up @@ -1793,8 +1797,8 @@ p, li { white-space: pre-wrap; }
<rect>
<x>0</x>
<y>0</y>
<width>343</width>
<height>684</height>
<width>629</width>
<height>793</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_12">
Expand Down Expand Up @@ -2315,6 +2319,23 @@ p, li { white-space: pre-wrap; }
<extends>QComboBox</extends>
<header>qgsblendmodecombobox.h</header>
</customwidget>
<customwidget>
<class>QgsOpacityWidget</class>
<extends>QWidget</extends>
<header>qgsopacitywidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsRasterBandComboBox</class>
<extends>QComboBox</extends>
<header>qgsrasterbandcombobox.h</header>
</customwidget>
<customwidget>
<class>QgsImageSourceLineEdit</class>
<extends>QWidget</extends>
<header>qgsfilecontentsourcelineedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mSearchLineEdit</tabstop>
Expand Down
13 changes: 13 additions & 0 deletions tests/src/python/test_qgsserver_wms_getlegendgraphic.py
Expand Up @@ -1045,6 +1045,19 @@ def testJsonSymbolMaxMinScale(self):
self.assertEqual(node['scaleMaxDenom'], 1000)
self.assertEqual(node['scaleMinDenom'], 10000)

def testLegendPlaceholderIcon(self):
qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": self.testdata_path + 'test_project_legend_placeholder_image.qgs',
"SERVICE": "WMS",
"VERSION": "1.3",
"REQUEST": "GetLegendGraphic",
"LAYER": "landsat",
"FORMAT": "image/png",
}.items())])

r, h = self._result(self._execute_request(qs))
self._img_diff_error(r, h, "WMS_GetLegendGraphic_Legend_Placeholder_Icon")


if __name__ == '__main__':
unittest.main()
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ec374d5

Please sign in to comment.