QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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 "qgsmediancut.h"
25#include "qgsmodule.h"
26#include "qgsproject.h"
29
30#include <QRegularExpression>
31
32namespace QgsWms
33{
35 {
36 return QStringLiteral( "1.3.0" );
37 }
38
39 QUrl serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
40 {
41 QUrl href;
42 href.setUrl( QgsServerProjectUtils::wmsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
43
44 // Build default url
45 if ( href.isEmpty() )
46 {
47 static const QSet<QString> sFilter {
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 || format.compare( QLatin1String( "image/png" ), Qt::CaseInsensitive ) == 0 )
77 {
79 }
80 else if ( format.compare( QLatin1String( "jpg " ), Qt::CaseInsensitive ) == 0 || format.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 )
81 {
83 }
84 else if ( format.compare( QLatin1String( "webp" ), Qt::CaseInsensitive ) == 0 || format.compare( QLatin1String( "image/webp" ), Qt::CaseInsensitive ) == 0 )
85 {
87 }
88 else
89 {
90 // lookup for png with mode
91 const thread_local QRegularExpression modeExpr = QRegularExpression( QStringLiteral( "image/png\\s*;\\s*mode=([^;]+)" ), QRegularExpression::CaseInsensitiveOption );
92
93 const QRegularExpressionMatch match = modeExpr.match( format );
94 const QString mode = match.captured( 1 );
95 if ( mode.compare( QLatin1String( "16bit" ), Qt::CaseInsensitive ) == 0 )
97 if ( mode.compare( QLatin1String( "8bit" ), Qt::CaseInsensitive ) == 0 )
99 if ( mode.compare( QLatin1String( "1bit" ), Qt::CaseInsensitive ) == 0 )
101 }
102
104 }
105
106 // Write image response
107 void writeImage( QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality )
108 {
109 const ImageOutputFormat outputFormat = parseImageFormat( formatStr );
110 QImage result;
111 QString saveFormat;
112 QString contentType;
113 switch ( outputFormat )
114 {
116 result = img;
117 contentType = QStringLiteral( "image/png" );
118 saveFormat = QStringLiteral( "PNG" );
119 break;
121 {
122 QVector<QRgb> colorTable;
123
124 // Rendering is made with the format QImage::Format_ARGB32_Premultiplied
125 // So we need to convert it in QImage::Format_ARGB32 in order to properly build
126 // the color table.
127 const QImage img256 = img.convertToFormat( QImage::Format_ARGB32 );
128 medianCut( colorTable, 256, img256 );
129 result = img256.convertToFormat( QImage::Format_Indexed8, colorTable, Qt::ColorOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
130 }
131 contentType = QStringLiteral( "image/png" );
132 saveFormat = QStringLiteral( "PNG" );
133 break;
135 result = img.convertToFormat( QImage::Format_ARGB4444_Premultiplied );
136 contentType = QStringLiteral( "image/png" );
137 saveFormat = QStringLiteral( "PNG" );
138 break;
140 result = img.convertToFormat( QImage::Format_Mono, Qt::MonoOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
141 contentType = QStringLiteral( "image/png" );
142 saveFormat = QStringLiteral( "PNG" );
143 break;
145 result = img;
146 contentType = QStringLiteral( "image/jpeg" );
147 saveFormat = QStringLiteral( "JPEG" );
148 break;
150 result = img;
151 contentType = QStringLiteral( "image/webp" );
152 saveFormat = QStringLiteral( "WEBP" );
153 break;
155 QgsMessageLog::logMessage( QStringLiteral( "Unsupported format string %1" ).arg( formatStr ) );
156 saveFormat = QStringLiteral( "Unknown" );
157 break;
158 }
159
160 // Preserve DPI, some conversions, in particular the one for 8bit will drop this information
161 result.setDotsPerMeterX( img.dotsPerMeterX() );
162 result.setDotsPerMeterY( img.dotsPerMeterY() );
163
164 if ( outputFormat != ImageOutputFormat::Unknown )
165 {
166 response.setHeader( "Content-Type", contentType );
167 if ( saveFormat == QLatin1String( "JPEG" ) || saveFormat == QLatin1String( "WEBP" ) )
168 {
169 result.save( response.io(), qPrintable( saveFormat ), imageQuality );
170 }
171 else
172 {
173 result.save( response.io(), qPrintable( saveFormat ) );
174 }
175 }
176 else
177 {
179 parameter.mValue = formatStr;
181 }
182 }
183} // namespace QgsWms
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())
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:109
static QgsProject * instance()
Returns the QgsProject singleton instance.
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 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.
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.
Definition qgswmsutils.h:42
ImageOutputFormat parseImageFormat(const QString &format)
Parse image format parameter.
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Returns WMS service URL.