QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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 <QRegularExpression>
23
24#include "qgsmodule.h"
25#include "qgswmsutils.h"
26#include "qgsmediancut.h"
29#include "qgsproject.h"
30
31namespace QgsWms
32{
34 {
35 return QStringLiteral( "1.3.0" );
36 }
37
38 QUrl serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
39 {
40 QUrl href;
41 href.setUrl( QgsServerProjectUtils::wmsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
42
43 // Build default url
44 if ( href.isEmpty() )
45 {
46 static const QSet<QString> sFilter
47 {
48 QStringLiteral( "REQUEST" ),
49 QStringLiteral( "VERSION" ),
50 QStringLiteral( "SERVICE" ),
51 QStringLiteral( "LAYERS" ),
52 QStringLiteral( "STYLES" ),
53 QStringLiteral( "SLD_VERSION" ),
54 QStringLiteral( "_DC" )
55 };
56
57 href = request.originalUrl();
58 QUrlQuery q( href );
59
60 const QList<QPair<QString, QString> > queryItems = q.queryItems();
61 for ( const QPair<QString, QString> &param : queryItems )
62 {
63 if ( sFilter.contains( param.first.toUpper() ) )
64 q.removeAllQueryItems( param.first );
65 }
66
67 href.setQuery( q );
68 }
69
70 return href;
71 }
72
73
74 ImageOutputFormat parseImageFormat( const QString &format )
75 {
76 if ( format.compare( QLatin1String( "png" ), Qt::CaseInsensitive ) == 0 ||
77 format.compare( QLatin1String( "image/png" ), Qt::CaseInsensitive ) == 0 )
78 {
80 }
81 else if ( format.compare( QLatin1String( "jpg " ), Qt::CaseInsensitive ) == 0 ||
82 format.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 )
83 {
85 }
86 else if ( format.compare( QLatin1String( "webp" ), Qt::CaseInsensitive ) == 0 ||
87 format.compare( QLatin1String( "image/webp" ), Qt::CaseInsensitive ) == 0 )
88 {
90 }
91 else
92 {
93 // lookup for png with mode
94 const thread_local QRegularExpression modeExpr = QRegularExpression( QStringLiteral( "image/png\\s*;\\s*mode=([^;]+)" ),
95 QRegularExpression::CaseInsensitiveOption );
96
97 const QRegularExpressionMatch match = modeExpr.match( format );
98 const QString mode = match.captured( 1 );
99 if ( mode.compare( QLatin1String( "16bit" ), Qt::CaseInsensitive ) == 0 )
101 if ( mode.compare( QLatin1String( "8bit" ), Qt::CaseInsensitive ) == 0 )
103 if ( mode.compare( QLatin1String( "1bit" ), Qt::CaseInsensitive ) == 0 )
105 }
106
108 }
109
110 // Write image response
111 void writeImage( QgsServerResponse &response, QImage &img, const QString &formatStr,
112 int imageQuality )
113 {
114 const ImageOutputFormat outputFormat = parseImageFormat( formatStr );
115 QImage result;
116 QString saveFormat;
117 QString contentType;
118 switch ( outputFormat )
119 {
121 result = img;
122 contentType = QStringLiteral( "image/png" );
123 saveFormat = QStringLiteral( "PNG" );
124 break;
126 {
127 QVector<QRgb> colorTable;
128
129 // Rendering is made with the format QImage::Format_ARGB32_Premultiplied
130 // So we need to convert it in QImage::Format_ARGB32 in order to properly build
131 // the color table.
132 const QImage img256 = img.convertToFormat( QImage::Format_ARGB32 );
133 medianCut( colorTable, 256, img256 );
134 result = img256.convertToFormat( QImage::Format_Indexed8, colorTable,
135 Qt::ColorOnly | Qt::ThresholdDither |
136 Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
137 }
138 contentType = QStringLiteral( "image/png" );
139 saveFormat = QStringLiteral( "PNG" );
140 break;
142 result = img.convertToFormat( QImage::Format_ARGB4444_Premultiplied );
143 contentType = QStringLiteral( "image/png" );
144 saveFormat = QStringLiteral( "PNG" );
145 break;
147 result = img.convertToFormat( QImage::Format_Mono,
148 Qt::MonoOnly | Qt::ThresholdDither |
149 Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
150 contentType = QStringLiteral( "image/png" );
151 saveFormat = QStringLiteral( "PNG" );
152 break;
154 result = img;
155 contentType = QStringLiteral( "image/jpeg" );
156 saveFormat = QStringLiteral( "JPEG" );
157 break;
159 result = img;
160 contentType = QStringLiteral( "image/webp" );
161 saveFormat = QStringLiteral( "WEBP" );
162 break;
164 QgsMessageLog::logMessage( QStringLiteral( "Unsupported format string %1" ).arg( formatStr ) );
165 saveFormat = QStringLiteral( "Unknown" );
166 break;
167 }
168
169 // Preserve DPI, some conversions, in particular the one for 8bit will drop this information
170 result.setDotsPerMeterX( img.dotsPerMeterX() );
171 result.setDotsPerMeterY( img.dotsPerMeterY() );
172
173 if ( outputFormat != ImageOutputFormat::Unknown )
174 {
175 response.setHeader( "Content-Type", contentType );
176 if ( saveFormat == QLatin1String( "JPEG" ) || saveFormat == QLatin1String( "WEBP" ) )
177 {
178 result.save( response.io(), qPrintable( saveFormat ), imageQuality );
179 }
180 else
181 {
182 result.save( response.io(), qPrintable( saveFormat ) );
183 }
184 }
185 else
186 {
188 parameter.mValue = formatStr;
190 parameter );
191 }
192 }
193} // namespace QgsWms
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
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:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
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...
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
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 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.
SERVER_EXPORT QString wmsServiceUrl(const QgsProject &project, const QgsServerRequest &request=QgsServerRequest(), const QgsServerSettings &settings=QgsServerSettings())
Returns the WMS service url.
Median cut implementation.
void writeImage(QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality)
Write image response.
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.
ImageOutputFormat
Supported image output format.
Definition qgswmsutils.h:41
@ Unknown
Unknown/invalid format.
ImageOutputFormat parseImageFormat(const QString &format)
Parse image format parameter.
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Returns WMS service URL.