Merge pull request #43927 from mhugent/raster_legend_placeholder_icon

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]
This commit is contained in:
mhugent 2021-07-03 15:12:18 +02:00 committed by GitHub
commit ec374d5ad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 3406 additions and 31 deletions

View File

@ -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:

View File

@ -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;
}
}

View File

@ -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
@ -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 );
}

View File

@ -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:
/**
@ -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;
};

View File

@ -14,7 +14,8 @@
***************************************************************************/
#include "qgsmaplayerlegend.h"
#include "qgsiconutils.h"
#include "qgsimagecache.h"
#include "qgssettings.h"
#include "qgslayertree.h"
#include "qgslayertreemodellegendnode.h"
@ -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;
@ -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;
}

View File

@ -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();
@ -946,6 +948,7 @@ void QgsRasterLayerProperties::apply()
/*
* Legend Tab
*/
mRasterLayer->setLegendPlaceholderImage( mLegendPlaceholderWidget->source() );
mLegendConfigEmbeddedWidget->applyToLayer();
QgsDebugMsgLevel( QStringLiteral( "apply processing symbology tab" ), 3 );

View File

@ -21,6 +21,7 @@
#include <QTreeWidget>
#include "qgsexpressionbuilderdialog.h"
#include "qgsfilecontentsourcelineedit.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerlegend.h"
#include "qgsrenderer.h"
@ -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 );
@ -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()
@ -221,6 +234,8 @@ void QgsVectorLayerLegendWidget::applyToLayer()
applyLabelLegend();
}
mLayer->setLegendPlaceholderImage( mImageSourceLineEdit->source() );
mLayer->setLegend( legend );
}

View File

@ -25,6 +25,7 @@
#include "qgis_gui.h"
class QCheckBox;
class QgsImageSourceLineEdit;
class QLabel;
class QPushButton;
class QTreeView;
@ -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;

View File

@ -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">
@ -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">
@ -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">
@ -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>
@ -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>
@ -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">
@ -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">
@ -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>

View File

@ -1045,6 +1045,19 @@ class TestQgsServerWMSGetLegendGraphic(TestQgsServerWMSTestBase):
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()

File diff suppressed because one or more lines are too long