QGIS API Documentation 3.99.0-Master (8e76e220402)
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#include <QString>
32
33using namespace Qt::StringLiterals;
34
35namespace QgsWms
36{
38 {
39 return u"1.3.0"_s;
40 }
41
42 QUrl serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
43 {
44 QUrl href;
45 href.setUrl( QgsServerProjectUtils::wmsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
46
47 // Build default url
48 if ( href.isEmpty() )
49 {
50 static const QSet<QString> sFilter {
51 u"REQUEST"_s,
52 u"VERSION"_s,
53 u"SERVICE"_s,
54 u"LAYERS"_s,
55 u"STYLES"_s,
56 u"SLD_VERSION"_s,
57 u"_DC"_s
58 };
59
60 href = request.originalUrl();
61 QUrlQuery q( href );
62
63 const QList<QPair<QString, QString>> queryItems = q.queryItems();
64 for ( const QPair<QString, QString> &param : queryItems )
65 {
66 if ( sFilter.contains( param.first.toUpper() ) )
67 q.removeAllQueryItems( param.first );
68 }
69
70 href.setQuery( q );
71 }
72
73 return href;
74 }
75
76
77 ImageOutputFormat parseImageFormat( const QString &format )
78 {
79 if ( format.compare( "png"_L1, Qt::CaseInsensitive ) == 0 || format.compare( "image/png"_L1, Qt::CaseInsensitive ) == 0 )
80 {
82 }
83 else if ( format.compare( "jpg "_L1, Qt::CaseInsensitive ) == 0 || format.compare( "image/jpeg"_L1, Qt::CaseInsensitive ) == 0 )
84 {
86 }
87 else if ( format.compare( "webp"_L1, Qt::CaseInsensitive ) == 0 || format.compare( "image/webp"_L1, Qt::CaseInsensitive ) == 0 )
88 {
90 }
91 else
92 {
93 // lookup for png with mode
94 const thread_local QRegularExpression modeExpr = QRegularExpression( u"image/png\\s*;\\s*mode=([^;]+)"_s, QRegularExpression::CaseInsensitiveOption );
95
96 const QRegularExpressionMatch match = modeExpr.match( format );
97 const QString mode = match.captured( 1 );
98 if ( mode.compare( "16bit"_L1, Qt::CaseInsensitive ) == 0 )
100 if ( mode.compare( "8bit"_L1, Qt::CaseInsensitive ) == 0 )
102 if ( mode.compare( "1bit"_L1, Qt::CaseInsensitive ) == 0 )
104 }
105
107 }
108
109 // Write image response
110 void writeImage( QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality )
111 {
112 const ImageOutputFormat outputFormat = parseImageFormat( formatStr );
113 QImage result;
114 QString saveFormat;
115 QString contentType;
116 switch ( outputFormat )
117 {
119 result = img;
120 contentType = u"image/png"_s;
121 saveFormat = u"PNG"_s;
122 break;
124 {
125 QVector<QRgb> colorTable;
126
127 // Rendering is made with the format QImage::Format_ARGB32_Premultiplied
128 // So we need to convert it in QImage::Format_ARGB32 in order to properly build
129 // the color table.
130 const QImage img256 = img.convertToFormat( QImage::Format_ARGB32 );
131 medianCut( colorTable, 256, img256 );
132 result = img256.convertToFormat( QImage::Format_Indexed8, colorTable, Qt::ColorOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
133 }
134 contentType = u"image/png"_s;
135 saveFormat = u"PNG"_s;
136 break;
138 result = img.convertToFormat( QImage::Format_ARGB4444_Premultiplied );
139 contentType = u"image/png"_s;
140 saveFormat = u"PNG"_s;
141 break;
143 result = img.convertToFormat( QImage::Format_Mono, Qt::MonoOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
144 contentType = u"image/png"_s;
145 saveFormat = u"PNG"_s;
146 break;
148 result = img;
149 contentType = u"image/jpeg"_s;
150 saveFormat = u"JPEG"_s;
151 break;
153 result = img;
154 contentType = u"image/webp"_s;
155 saveFormat = u"WEBP"_s;
156 break;
158 QgsMessageLog::logMessage( u"Unsupported format string %1"_s.arg( formatStr ) );
159 saveFormat = u"Unknown"_s;
160 break;
161 }
162
163 // Preserve DPI, some conversions, in particular the one for 8bit will drop this information
164 result.setDotsPerMeterX( img.dotsPerMeterX() );
165 result.setDotsPerMeterY( img.dotsPerMeterY() );
166
167 if ( outputFormat != ImageOutputFormat::Unknown )
168 {
169 response.setHeader( "Content-Type", contentType );
170 if ( saveFormat == "JPEG"_L1 || saveFormat == "WEBP"_L1 )
171 {
172 result.save( response.io(), qPrintable( saveFormat ), imageQuality );
173 }
174 else
175 {
176 result.save( response.io(), qPrintable( saveFormat ) );
177 }
178 }
179 else
180 {
182 parameter.mValue = formatStr;
184 }
185 }
186} // 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:113
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.