QGIS API Documentation  3.20.0-Odense (decaadbb31)
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"
27 #include "qgsserverprojectutils.h"
28 #include "qgswmsserviceexception.h"
29 #include "qgsproject.h"
30 
31 namespace QgsWms
32 {
33  QUrl serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
34  {
35  QUrl href;
36  href.setUrl( QgsServerProjectUtils::wmsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
37 
38  // Build default url
39  if ( href.isEmpty() )
40  {
41  static QSet<QString> sFilter
42  {
43  QStringLiteral( "REQUEST" ),
44  QStringLiteral( "VERSION" ),
45  QStringLiteral( "SERVICE" ),
46  QStringLiteral( "LAYERS" ),
47  QStringLiteral( "STYLES" ),
48  QStringLiteral( "SLD_VERSION" ),
49  QStringLiteral( "_DC" )
50  };
51 
52  href = request.originalUrl();
53  QUrlQuery q( href );
54 
55  for ( auto param : q.queryItems() )
56  {
57  if ( sFilter.contains( param.first.toUpper() ) )
58  q.removeAllQueryItems( param.first );
59  }
60 
61  href.setQuery( q );
62  }
63 
64  return href;
65  }
66 
67 
68  ImageOutputFormat parseImageFormat( const QString &format )
69  {
70  if ( format.compare( QLatin1String( "png" ), Qt::CaseInsensitive ) == 0 ||
71  format.compare( QLatin1String( "image/png" ), Qt::CaseInsensitive ) == 0 )
72  {
73  return PNG;
74  }
75  else if ( format.compare( QLatin1String( "jpg " ), Qt::CaseInsensitive ) == 0 ||
76  format.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 )
77  {
78  return JPEG;
79  }
80  else if ( format.compare( QLatin1String( "webp" ), Qt::CaseInsensitive ) == 0 ||
81  format.compare( QLatin1String( "image/webp" ), Qt::CaseInsensitive ) == 0 )
82  {
83  return WEBP;
84  }
85  else
86  {
87  // lookup for png with mode
88  QRegularExpression modeExpr = QRegularExpression( QStringLiteral( "image/png\\s*;\\s*mode=([^;]+)" ),
89  QRegularExpression::CaseInsensitiveOption );
90 
91  QRegularExpressionMatch match = modeExpr.match( format );
92  QString mode = match.captured( 1 );
93  if ( mode.compare( QLatin1String( "16bit" ), Qt::CaseInsensitive ) == 0 )
94  return PNG16;
95  if ( mode.compare( QLatin1String( "8bit" ), Qt::CaseInsensitive ) == 0 )
96  return PNG8;
97  if ( mode.compare( QLatin1String( "1bit" ), Qt::CaseInsensitive ) == 0 )
98  return PNG1;
99  }
100 
101  return UNKN;
102  }
103 
104  // Write image response
105  void writeImage( QgsServerResponse &response, QImage &img, const QString &formatStr,
106  int imageQuality )
107  {
108  ImageOutputFormat outputFormat = parseImageFormat( formatStr );
109  QImage result;
110  QString saveFormat;
111  QString contentType;
112  switch ( outputFormat )
113  {
114  case PNG:
115  result = img;
116  contentType = "image/png";
117  saveFormat = "PNG";
118  break;
119  case PNG8:
120  {
121  QVector<QRgb> colorTable;
122 
123  // Rendering is made with the format QImage::Format_ARGB32_Premultiplied
124  // So we need to convert it in QImage::Format_ARGB32 in order to properly build
125  // the color table.
126  QImage img256 = img.convertToFormat( QImage::Format_ARGB32 );
127  medianCut( colorTable, 256, img256 );
128  result = img256.convertToFormat( QImage::Format_Indexed8, colorTable,
129  Qt::ColorOnly | Qt::ThresholdDither |
130  Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
131  }
132  contentType = "image/png";
133  saveFormat = "PNG";
134  break;
135  case PNG16:
136  result = img.convertToFormat( QImage::Format_ARGB4444_Premultiplied );
137  contentType = "image/png";
138  saveFormat = "PNG";
139  break;
140  case PNG1:
141  result = img.convertToFormat( QImage::Format_Mono,
142  Qt::MonoOnly | Qt::ThresholdDither |
143  Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
144  contentType = "image/png";
145  saveFormat = "PNG";
146  break;
147  case JPEG:
148  result = img;
149  contentType = "image/jpeg";
150  saveFormat = "JPEG";
151  break;
152  case WEBP:
153  result = img;
154  contentType = QStringLiteral( "image/webp" );
155  saveFormat = QStringLiteral( "WEBP" );
156  break;
157  default:
158  QgsMessageLog::logMessage( QString( "Unsupported format string %1" ).arg( formatStr ) );
159  saveFormat = UNKN;
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 != UNKN )
168  {
169  response.setHeader( "Content-Type", contentType );
170  if ( saveFormat == QLatin1String( "JPEG" ) || saveFormat == QLatin1String( "WEBP" ) )
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  parameter );
185  }
186  }
187 } // 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:99
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
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.
ImageOutputFormat
Supported image output format.
Definition: qgswmsutils.h:41
ImageOutputFormat parseImageFormat(const QString &format)
Parse image format parameter.
Definition: qgswmsutils.cpp:68
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Returns WMS service URL.
Definition: qgswmsutils.cpp:33