QGIS API Documentation 4.1.0-Master (31622b25bb0)
Loading...
Searching...
No Matches
qgswmsutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgswmsutils.cpp
3 -------------------------
4 begin : December 20 , 2016
5 copyright : (C) 2007 by Marco Hugentobler ( parts from qgswmshandler)
6 (C) 2014 by Alessandro Pasotti ( parts from qgswmshandler)
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
22#include "qgswmsutils.h"
23
24#include "qgslayertree.h"
25#include "qgsmediancut.h"
26#include "qgsmodule.h"
27#include "qgsproject.h"
30
31#include <QRegularExpression>
32#include <QString>
33
34using namespace Qt::StringLiterals;
35
36namespace QgsWms
37{
39 {
40 return u"1.3.0"_s;
41 }
42
43 QUrl serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
44 {
45 QUrl href;
46 href.setUrl( QgsServerProjectUtils::wmsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
47
48 // Build default url
49 if ( href.isEmpty() )
50 {
51 static const QSet<QString> sFilter { u"REQUEST"_s, u"VERSION"_s, u"SERVICE"_s, u"LAYERS"_s, u"STYLES"_s, u"SLD_VERSION"_s, u"_DC"_s };
52
53 href = request.originalUrl();
54 QUrlQuery q( href );
55
56 const QList<QPair<QString, QString>> queryItems = q.queryItems();
57 for ( const QPair<QString, QString> &param : queryItems )
58 {
59 if ( sFilter.contains( param.first.toUpper() ) )
60 q.removeAllQueryItems( param.first );
61 }
62
63 href.setQuery( q );
64 }
65
66 return href;
67 }
68
69
70 ImageOutputFormat parseImageFormat( const QString &format )
71 {
72 if ( format.compare( "png"_L1, Qt::CaseInsensitive ) == 0 || format.compare( "image/png"_L1, Qt::CaseInsensitive ) == 0 )
73 {
75 }
76 else if ( format.compare( "jpg "_L1, Qt::CaseInsensitive ) == 0 || format.compare( "image/jpeg"_L1, Qt::CaseInsensitive ) == 0 )
77 {
79 }
80 else if ( format.compare( "webp"_L1, Qt::CaseInsensitive ) == 0 || format.compare( "image/webp"_L1, Qt::CaseInsensitive ) == 0 )
81 {
83 }
84 else
85 {
86 // lookup for png with mode
87 const thread_local QRegularExpression modeExpr = QRegularExpression( u"image/png\\s*;\\s*mode=([^;]+)"_s, QRegularExpression::CaseInsensitiveOption );
88
89 const QRegularExpressionMatch match = modeExpr.match( format );
90 const QString mode = match.captured( 1 );
91 if ( mode.compare( "16bit"_L1, Qt::CaseInsensitive ) == 0 )
93 if ( mode.compare( "8bit"_L1, Qt::CaseInsensitive ) == 0 )
95 if ( mode.compare( "1bit"_L1, Qt::CaseInsensitive ) == 0 )
97 }
98
100 }
101
102 // Write image response
103 void writeImage( QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality )
104 {
105 const ImageOutputFormat outputFormat = parseImageFormat( formatStr );
106 QImage result;
107 QString saveFormat;
108 QString contentType;
109 switch ( outputFormat )
110 {
112 result = img;
113 contentType = u"image/png"_s;
114 saveFormat = u"PNG"_s;
115 break;
117 {
118 QVector<QRgb> colorTable;
119
120 // Rendering is made with the format QImage::Format_ARGB32_Premultiplied
121 // So we need to convert it in QImage::Format_ARGB32 in order to properly build
122 // the color table.
123 const QImage img256 = img.convertToFormat( QImage::Format_ARGB32 );
124 medianCut( colorTable, 256, img256 );
125 result = img256.convertToFormat( QImage::Format_Indexed8, colorTable, Qt::ColorOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
126 }
127 contentType = u"image/png"_s;
128 saveFormat = u"PNG"_s;
129 break;
131 result = img.convertToFormat( QImage::Format_ARGB4444_Premultiplied );
132 contentType = u"image/png"_s;
133 saveFormat = u"PNG"_s;
134 break;
136 result = img.convertToFormat( QImage::Format_Mono, Qt::MonoOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
137 contentType = u"image/png"_s;
138 saveFormat = u"PNG"_s;
139 break;
141 result = img;
142 contentType = u"image/jpeg"_s;
143 saveFormat = u"JPEG"_s;
144 break;
146 result = img;
147 contentType = u"image/webp"_s;
148 saveFormat = u"WEBP"_s;
149 break;
151 QgsMessageLog::logMessage( u"Unsupported format string %1"_s.arg( formatStr ) );
152 saveFormat = u"Unknown"_s;
153 break;
154 }
155
156 // Preserve DPI, some conversions, in particular the one for 8bit will drop this information
157 result.setDotsPerMeterX( img.dotsPerMeterX() );
158 result.setDotsPerMeterY( img.dotsPerMeterY() );
159
160 if ( outputFormat != ImageOutputFormat::Unknown )
161 {
162 response.setHeader( "Content-Type", contentType );
163 if ( saveFormat == "JPEG"_L1 || saveFormat == "WEBP"_L1 )
164 {
165 result.save( response.io(), qPrintable( saveFormat ), imageQuality );
166 }
167 else
168 {
169 result.save( response.io(), qPrintable( saveFormat ) );
170 }
171 }
172 else
173 {
175 parameter.mValue = formatStr;
177 }
178 }
179
189 QHash<const QgsMapLayer *, QStringList> &acceptableLayersAndRequestNames,
190 const QgsProject &project,
191 const QStringList &requestedLayerNames,
192 const QgsLayerTreeGroup *group,
193 QStringList requestedParentNames = QStringList(),
194 bool groupIsAnOpaqueChild = false
195 )
196 {
197 //get group nickname
198 QString groupName = group->serverProperties()->shortName();
199 if ( groupName.isEmpty() )
200 groupName = group->name();
201
202 bool projectIsRequested = ( requestedLayerNames.contains( QgsServerProjectUtils::wmsRootName( project ) ) || requestedLayerNames.contains( project.title() ) );
203 bool groupIsRequested = requestedLayerNames.contains( groupName );
204
205 // append the group to the list, when it's explicitly requested and it's not already a child of an opaque group
206 if ( groupIsRequested && !groupIsAnOpaqueChild )
207 requestedParentNames << groupName;
208
209 // the group should not be opaque or explicitly requested (by the groupname or by the project name)
210 if ( ( group->wmsGroupRequestMode() != Qgis::WmsGroupRequestMode::Opaque ) || groupIsRequested || projectIsRequested )
211 {
212 // when the current group is opaque or the previous groups have been opaque it is an opaque child and should not be requestable
213 bool isOpaqueChild = ( group->wmsGroupRequestMode() == Qgis::WmsGroupRequestMode::Opaque ) || groupIsAnOpaqueChild;
214
215 for ( QgsLayerTreeNode *child : group->children() )
216 {
217 if ( QgsLayerTree::isGroup( child ) )
218 {
219 auto subgroup = static_cast<const QgsLayerTreeGroup *>( child );
220 _collectAcceptableLayersAndRequestNames( acceptableLayersAndRequestNames, project, requestedLayerNames, subgroup, requestedParentNames, isOpaqueChild );
221 }
222 else if ( QgsLayerTree::isLayer( child ) )
223 {
224 auto layernode = static_cast<const QgsLayerTreeLayer *>( child );
225 const QgsMapLayer *layer = layernode->layer();
226 if ( !layer )
227 continue;
228
229 //get layer nickname
230 QString name = layer->serverProperties()->shortName();
232 {
233 name = layer->id();
234 }
235 else if ( name.isEmpty() )
236 {
237 name = layer->name();
238 }
239
240 QStringList requestedNames = requestedParentNames;
241 // when the layer is explicitly requested and it's not an opaque child, then add it to the requested names
242 if ( requestedLayerNames.contains( name ) && !isOpaqueChild )
243 {
244 requestedNames << name;
245 }
246 // we add the layer to the map when it's requested (or no requestedLayerNames are passed)
247 if ( !requestedNames.isEmpty() || requestedLayerNames.isEmpty() || projectIsRequested )
248 acceptableLayersAndRequestNames.insert( layer, requestedNames );
249 }
250 }
251 }
252 }
253
254 void collectAcceptableLayersAndRequestNames( QHash<const QgsMapLayer *, QStringList> &acceptableLayersAndRequestNames, const QgsProject &project, const QStringList &requestedLayerNames )
255 {
256 //Call function used for recursive collect based on the layer tree root
257 _collectAcceptableLayersAndRequestNames( acceptableLayersAndRequestNames, project, requestedLayerNames, project.layerTreeRoot() );
258 }
259
260} // namespace QgsWms
@ Opaque
Group can be requested, children cannot (appears like a single layer).
Definition qgis.h:6766
Layer tree group node serves as a container for layers and further groups.
QString name() const override
Returns the group's name.
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the layer tree group.
Qgis::WmsGroupRequestMode wmsGroupRequestMode() const
Returns the request mode of the group.
Layer tree node points to a map layer.
Base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:86
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
static QgsProject * instance()
Returns the QgsProject singleton instance.
QString title
Definition qgsproject.h:117
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
static QString wmsRootName(const QgsProject &project)
Returns the WMS root layer name defined in a QGIS project.
static bool wmsUseLayerIds(const QgsProject &project)
Returns if layer ids are used as name in WMS.
static QString wmsServiceUrl(const QgsProject &project, const QgsServerRequest &request=QgsServerRequest(), const QgsServerSettings &settings=QgsServerSettings())
Returns the WMS service url.
Defines requests passed to QgsService classes.
QUrl originalUrl() const
Returns the request url as seen by the web server, by default this is equal to the url seen by QGIS s...
Defines the response interface passed to QgsService.
virtual void setHeader(const QString &key, const QString &value)=0
Set a single header value replacing any existing value(s) for the same key.
virtual QIODevice * io()=0
Returns the underlying QIODevice.
Provides a way to retrieve settings by prioritizing according to environment variables,...
Exception thrown in case of malformed request.
WMS parameter received from the client.
Median cut implementation.
void writeImage(QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality)
Write image response.
void collectAcceptableLayersAndRequestNames(QHash< const QgsMapLayer *, QStringList > &acceptableLayersAndRequestNames, const QgsProject &project, const QStringList &requestedLayerNames)
Collects the acceptableLayersAndRequestNames, a hash of all the layers that can be rendered and for e...
void medianCut(QVector< QRgb > &colorTable, int nColors, const QImage &inputImage)
Median cut implementation used when reducing RGB colors to palletized colors.
QString implementationVersion()
Returns the highest version supported by this implementation.
void _collectAcceptableLayersAndRequestNames(QHash< const QgsMapLayer *, QStringList > &acceptableLayersAndRequestNames, const QgsProject &project, const QStringList &requestedLayerNames, const QgsLayerTreeGroup *group, QStringList requestedParentNames=QStringList(), bool groupIsAnOpaqueChild=false)
Collects the acceptableLayersAndRequestNames recursively, a hash of all the layers that can be render...
ImageOutputFormat
Supported image output format.
Definition qgswmsutils.h:42
@ Unknown
Unknown/invalid format.
Definition qgswmsutils.h:43
ImageOutputFormat parseImageFormat(const QString &format)
Parse image format parameter.
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Returns WMS service URL.