QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgswmsgetlegendgraphics.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgswmsgetlegendgraphics.cpp
3  -------------------------
4  begin : December 20 , 2016
5  copyright : (C) 2007 by Marco Hugentobler (original code)
6  (C) 2014 by Alessandro Pasotti (original code)
7  (C) 2016 by David Marteau
8  email : marco dot hugentobler at karto dot baug dot ethz dot ch
9  a dot pasotti at itopen dot it
10  david dot marteau at 3liz dot com
11  ***************************************************************************/
12 
13 /***************************************************************************
14  * *
15  * This program is free software; you can redistribute it and/or modify *
16  * it under the terms of the GNU General Public License as published by *
17  * the Free Software Foundation; either version 2 of the License, or *
18  * (at your option) any later version. *
19  * *
20  ***************************************************************************/
21 #include "qgslayertree.h"
22 #include "qgslegendrenderer.h"
23 #include "qgsvectorlayer.h"
25 #include "qgssymbollayerutils.h"
26 #include "qgsmaplayerlegend.h"
27 
28 #include "qgswmsutils.h"
29 #include "qgswmsserviceexception.h"
31 #include "qgswmsrenderer.h"
32 
33 #include <QImage>
34 
35 namespace QgsWms
36 {
37  void writeGetLegendGraphics( QgsServerInterface *serverIface, const QgsProject *project,
38  const QString &, const QgsServerRequest &request,
39  QgsServerResponse &response )
40  {
41  // get parameters from query
42  QgsWmsParameters parameters( QUrlQuery( request.url() ) );
43 
44  // check parameters validity
45  checkParameters( parameters );
46 
47  // init render context
48  QgsWmsRenderContext context( project, serverIface );
51  context.setParameters( parameters );
52 
53  const QString format = request.parameters().value( QStringLiteral( "FORMAT" ), QStringLiteral( "PNG" ) );
54  ImageOutputFormat outputFormat = parseImageFormat( format );
55 
56  QString saveFormat;
57  QString contentType;
58  switch ( outputFormat )
59  {
60  case PNG:
61  case PNG8:
62  case PNG16:
63  case PNG1:
64  contentType = "image/png";
65  saveFormat = "PNG";
66  break;
67  case JPEG:
68  contentType = "image/jpeg";
69  saveFormat = "JPEG";
70  break;
71  default:
72  throw QgsServiceException( "InvalidFormat",
73  QStringLiteral( "Output format '%1' is not supported in the GetLegendGraphic request" ).arg( format ) );
74  break;
75  }
76 
77  // Get cached image
78 #ifdef HAVE_SERVER_PYTHON_PLUGINS
79  QgsAccessControl *accessControl = serverIface->accessControls();
80  QgsServerCacheManager *cacheManager = serverIface->cacheManager();
81  if ( cacheManager )
82  {
83  QImage image;
84  QByteArray content = cacheManager->getCachedImage( project, request, accessControl );
85  if ( !content.isEmpty() && image.loadFromData( content ) )
86  {
87  response.setHeader( QStringLiteral( "Content-Type" ), contentType );
88  image.save( response.io(), qPrintable( saveFormat ) );
89  return;
90  }
91  }
92 #endif
93  QgsRenderer renderer( context );
94 
95  // retrieve legend settings and model
96  std::unique_ptr<QgsLayerTree> tree( layerTree( context ) );
97  std::unique_ptr<QgsLayerTreeModel> model( legendModel( context, *tree.get() ) );
98 
99  // rendering
100  std::unique_ptr<QImage> result;
101  if ( !parameters.rule().isEmpty() )
102  {
103  QgsLayerTreeModelLegendNode *node = legendNode( parameters.rule(), *model.get() );
104  result.reset( renderer.getLegendGraphics( *node ) );
105  }
106  else
107  {
108  result.reset( renderer.getLegendGraphics( *model.get() ) );
109  }
110 
111  tree->clear();
112 
113  if ( result )
114  {
115  writeImage( response, *result, format, context.imageQuality() );
116 #ifdef HAVE_SERVER_PYTHON_PLUGINS
117  if ( cacheManager )
118  {
119  QByteArray content = response.data();
120  if ( !content.isEmpty() )
121  cacheManager->setCachedImage( &content, project, request, accessControl );
122  }
123 #endif
124  }
125  else
126  {
127  throw QgsException( QStringLiteral( "Failed to compute GetLegendGraphics image" ) );
128  }
129  }
130 
131  void checkParameters( const QgsWmsParameters &parameters )
132  {
133  if ( parameters.allLayersNickname().isEmpty() )
134  {
136  parameters[QgsWmsParameter::LAYERS] );
137  }
138 
139  if ( parameters.format() == QgsWmsParameters::Format::NONE )
140  {
142  parameters[QgsWmsParameter::FORMAT] );
143  }
144 
145  if ( ! parameters.bbox().isEmpty() && !parameters.rule().isEmpty() )
146  {
148  QStringLiteral( "BBOX parameter cannot be combined with RULE." ) );
149  }
150 
151  if ( ! parameters.bbox().isEmpty() && parameters.bboxAsRectangle().isEmpty() )
152  {
154  parameters[QgsWmsParameter::BBOX] );
155  }
156  }
157 
159  {
160  const QgsWmsParameters parameters = context.parameters();
161  std::unique_ptr<QgsLayerTreeModel> model( new QgsLayerTreeModel( &tree ) );
162 
163  if ( context.scaleDenominator() > 0 )
164  {
165  model->setLegendFilterByScale( context.scaleDenominator() );
166  }
167 
168  // content based legend
169  if ( ! parameters.bbox().isEmpty() )
170  {
171  QgsRenderer renderer( context );
172  const QgsRenderer::HitTest symbols = renderer.symbols();
173 
174  for ( QgsLayerTreeNode *node : tree.children() )
175  {
176  QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
177 
178  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer->layer() );
179  if ( !vl || !vl->renderer() )
180  continue;
181 
182  QList<int> order;
183  int i = 0;
184 
185  for ( const QgsLegendSymbolItem &item : vl->renderer()->legendSymbolItems() )
186  {
187  const QString prop = QgsSymbolLayerUtils::symbolProperties( item.legacyRuleKey() );
188  if ( symbols[vl].contains( prop ) )
189  {
190  order.append( i );
191  }
192  ++i;
193  }
194 
195  // either remove the whole layer or just filter out some items
196  if ( order.isEmpty() )
197  {
198  tree.removeChildNode( layer );
199  }
200  else
201  {
203  model->refreshLayerLegend( layer );
204  }
205  }
206  }
207 
208  // if legend is not based on rendering rules
209  if ( parameters.rule().isEmpty() )
210  {
211  QList<QgsLayerTreeNode *> children = tree.children();
212  for ( QgsLayerTreeNode *node : children )
213  {
214  if ( ! QgsLayerTree::isLayer( node ) )
215  continue;
216 
217  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
218 
219  // layer titles - hidden or not
221 
222  // rule item titles
223  if ( !parameters.ruleLabelAsBool() )
224  {
225  for ( QgsLayerTreeModelLegendNode *legendNode : model->layerLegendNodes( nodeLayer ) )
226  {
227  // empty string = no override, so let's use one space
228  legendNode->setUserLabel( QStringLiteral( " " ) );
229  }
230  }
231  else if ( !parameters.layerTitleAsBool() )
232  {
233  for ( QgsLayerTreeModelLegendNode *legendNode : model->layerLegendNodes( nodeLayer ) )
234  {
237  }
238  }
239  }
240  }
241 
242  return model.release();
243  }
244 
246  {
247  std::unique_ptr<QgsLayerTree> tree( new QgsLayerTree() );
248 
249  QList<QgsVectorLayerFeatureCounter *> counters;
250  for ( QgsMapLayer *ml : context.layersToRender() )
251  {
252  QgsLayerTreeLayer *lt = tree->addLayer( ml );
253  lt->setUseLayerName( false ); // do not modify underlying layer
254 
255  // name
256  if ( !ml->title().isEmpty() )
257  lt->setName( ml->title() );
258 
259  // show feature count
260  const bool showFeatureCount = context.parameters().showFeatureCountAsBool();
261  const QString property = QStringLiteral( "showFeatureCount" );
262  lt->setCustomProperty( property, showFeatureCount );
263 
264  if ( ml->type() != QgsMapLayerType::VectorLayer || !showFeatureCount )
265  continue;
266 
267  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
269  if ( !counter )
270  continue;
271 
272  counters.append( counter );
273  }
274 
275  for ( QgsVectorLayerFeatureCounter *counter : counters )
276  {
277  counter->waitForFinished();
278  }
279 
280  return tree.release();
281  }
282 
284  {
285  for ( QgsLayerTreeLayer *layer : model.rootGroup()->findLayers() )
286  {
287  for ( QgsLayerTreeModelLegendNode *node : model.layerLegendNodes( layer ) )
288  {
289  if ( node->data( Qt::DisplayRole ).toString().compare( rule ) == 0 )
290  return node;
291  }
292  }
293  return nullptr;
294  }
295 } // namespace QgsWms
296 
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:78
QList< QgsMapLayer * > layersToRender() const
Returns a list of all layers to actually render according to the current configuration.
HitTest symbols()
Returns the hit test according to the current context.
static void setLegendNodeOrder(QgsLayerTreeLayer *nodeLayer, const QList< int > &order)
QgsVectorLayerFeatureCounter * countSymbolFeatures()
Count features for symbols.
void setUseLayerName(bool use=true)
Uses the layer&#39;s name if use is true, or the name manually set if false.
QgsWmsParameters parameters() const
Returns WMS parameters.
QgsRectangle bboxAsRectangle() const
Returns BBOX as a rectangle if defined and valid.
bool setCachedImage(const QByteArray *img, const QgsProject *project, const QgsServerRequest &request, QgsAccessControl *accessControl) const
Updates or inserts the image in cache like tiles.
Counts the features in a QgsVectorLayer in task.
QgsLayerTree * layerTree(const QgsWmsRenderContext &context)
bool waitForFinished(int timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
Exception thrown in case of malformed request.
int imageQuality() const
Returns the image quality to use for rendering according to the current configuration.
void setFlag(Flag flag, bool on=true)
Sets or unsets a rendering flag according to the on value.
void setParameters(const QgsWmsParameters &parameters)
Sets WMS parameters.
QString bbox() const
Returns BBOX if defined or an empty string.
bool ruleLabelAsBool() const
Returns RULELABEL as a bool.
Exception class for WMS service exceptions.
bool showFeatureCountAsBool() const
Returns SHOWFEATURECOUNT as a bool.
void writeImage(QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality)
Write image response.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QHash< QgsVectorLayer *, SymbolSet > HitTest
QStringList allLayersNickname() const
Returns nickname of layers found in LAYER and LAYERS parameters.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
Provides an interface to retrieve and manipulate WMS parameters received from the client...
A helper class that centralizes caches accesses given by all the server cache filter plugins...
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
This class is a base class for nodes in a layer tree.
Reads and writes project states.
Definition: qgsproject.h:89
QgsFeatureRenderer * renderer()
Returns renderer.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
Format format() const
Returns format.
void writeGetLegendGraphics(QgsServerInterface *serverIface, const QgsProject *project, const QString &, const QgsServerRequest &request, QgsServerResponse &response)
Output GetLegendGRaphics response.
void setName(const QString &n) override
Sets the layer&#39;s name.
bool layerTitleAsBool() const
Returns LAYERTITLE as a bool or its default value if not defined.
Median cut implementation.
double scaleDenominator() const
Returns the scale denominator to use for rendering according to the current configuration.
virtual QByteArray data() const =0
Gets the data written so far.
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
void checkParameters(const QgsWmsParameters &parameters)
QgsMapLayer * layer() const
Returns the map layer associated with this node.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins...
Special style, item is hidden including margins around.
virtual void setEmbeddedInParent(bool embedded)
Map renderer for WMS requests.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QgsLayerTreeModel * legendModel(const QgsWmsRenderContext &context, QgsLayerTree &tree)
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
virtual QIODevice * io()=0
Returns the underlying QIODevice.
Rendering context for the WMS renderer.
virtual QgsServerCacheManager * cacheManager() const =0
Gets the registered server cache filters.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
A helper class that centralizes restrictions given by all the access control filter plugins...
ImageOutputFormat
Supported image output format.
Definition: qgswmsutils.h:39
QString rule() const
Returns RULE parameter or an empty string if none is defined.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Represents a vector layer which manages a vector based data sets.
Defines a QGIS exception class.
Definition: qgsexception.h:34
ImageOutputFormat parseImageFormat(const QString &format)
Parse image format parameter.
Definition: qgswmsutils.cpp:70
virtual void setUserLabel(const QString &userLabel)
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.
QImage * getLegendGraphics(QgsLayerTreeModel &model)
Returns the map legend as an image (or nullptr in case of error).
QByteArray getCachedImage(const QgsProject *project, const QgsServerRequest &request, QgsAccessControl *accessControl) const
Returns cached image (or 0 if image not in cache) like tiles.