QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 
30 namespace QgsWms
31 {
32  QUrl serviceUrl( const QgsServerRequest &request, const QgsProject *project )
33  {
34  QUrl href;
35  if ( project )
36  {
37  href.setUrl( QgsServerProjectUtils::wmsServiceUrl( *project ) );
38  }
39 
40  // Build default url
41  if ( href.isEmpty() )
42  {
43  static QSet<QString> sFilter
44  {
45  QStringLiteral( "REQUEST" ),
46  QStringLiteral( "VERSION" ),
47  QStringLiteral( "SERVICE" ),
48  QStringLiteral( "LAYERS" ),
49  QStringLiteral( "STYLES" ),
50  QStringLiteral( "SLD_VERSION" ),
51  QStringLiteral( "_DC" )
52  };
53 
54  href = request.originalUrl();
55  QUrlQuery q( href );
56 
57  for ( auto param : q.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( QLatin1String( "png" ), Qt::CaseInsensitive ) == 0 ||
73  format.compare( QLatin1String( "image/png" ), Qt::CaseInsensitive ) == 0 )
74  {
75  return PNG;
76  }
77  else if ( format.compare( QLatin1String( "jpg " ), Qt::CaseInsensitive ) == 0 ||
78  format.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 )
79  {
80  return JPEG;
81  }
82  else if ( format.compare( QLatin1String( "webp" ), Qt::CaseInsensitive ) == 0 ||
83  format.compare( QLatin1String( "image/webp" ), Qt::CaseInsensitive ) == 0 )
84  {
85  return WEBP;
86  }
87  else
88  {
89  // lookup for png with mode
90  QRegularExpression modeExpr = QRegularExpression( QStringLiteral( "image/png\\s*;\\s*mode=([^;]+)" ),
91  QRegularExpression::CaseInsensitiveOption );
92 
93  QRegularExpressionMatch match = modeExpr.match( format );
94  QString mode = match.captured( 1 );
95  if ( mode.compare( QLatin1String( "16bit" ), Qt::CaseInsensitive ) == 0 )
96  return PNG16;
97  if ( mode.compare( QLatin1String( "8bit" ), Qt::CaseInsensitive ) == 0 )
98  return PNG8;
99  if ( mode.compare( QLatin1String( "1bit" ), Qt::CaseInsensitive ) == 0 )
100  return PNG1;
101  }
102 
103  return UNKN;
104  }
105 
106  // Write image response
107  void writeImage( QgsServerResponse &response, QImage &img, const QString &formatStr,
108  int imageQuality )
109  {
110  ImageOutputFormat outputFormat = parseImageFormat( formatStr );
111  QImage result;
112  QString saveFormat;
113  QString contentType;
114  switch ( outputFormat )
115  {
116  case PNG:
117  result = img;
118  contentType = "image/png";
119  saveFormat = "PNG";
120  break;
121  case PNG8:
122  {
123  QVector<QRgb> colorTable;
124 
125  // Rendering is made with the format QImage::Format_ARGB32_Premultiplied
126  // So we need to convert it in QImage::Format_ARGB32 in order to properly build
127  // the color table.
128  QImage img256 = img.convertToFormat( QImage::Format_ARGB32 );
129  medianCut( colorTable, 256, img256 );
130  result = img256.convertToFormat( QImage::Format_Indexed8, colorTable,
131  Qt::ColorOnly | Qt::ThresholdDither |
132  Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
133  }
134  contentType = "image/png";
135  saveFormat = "PNG";
136  break;
137  case PNG16:
138  result = img.convertToFormat( QImage::Format_ARGB4444_Premultiplied );
139  contentType = "image/png";
140  saveFormat = "PNG";
141  break;
142  case PNG1:
143  result = img.convertToFormat( QImage::Format_Mono,
144  Qt::MonoOnly | Qt::ThresholdDither |
145  Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
146  contentType = "image/png";
147  saveFormat = "PNG";
148  break;
149  case JPEG:
150  result = img;
151  contentType = "image/jpeg";
152  saveFormat = "JPEG";
153  break;
154  case WEBP:
155  result = img;
156  contentType = QStringLiteral( "image/webp" );
157  saveFormat = QStringLiteral( "WEBP" );
158  break;
159  default:
160  QgsMessageLog::logMessage( QString( "Unsupported format string %1" ).arg( formatStr ) );
161  saveFormat = UNKN;
162  break;
163  }
164 
165  // Preserve DPI, some conversions, in particular the one for 8bit will drop this information
166  result.setDotsPerMeterX( img.dotsPerMeterX() );
167  result.setDotsPerMeterY( img.dotsPerMeterY() );
168 
169  if ( outputFormat != UNKN )
170  {
171  response.setHeader( "Content-Type", contentType );
172  if ( saveFormat == QLatin1String( "JPEG" ) || saveFormat == QLatin1String( "WEBP" ) )
173  {
174  result.save( response.io(), qPrintable( saveFormat ), imageQuality );
175  }
176  else
177  {
178  result.save( response.io(), qPrintable( saveFormat ) );
179  }
180  }
181  else
182  {
184  parameter.mValue = formatStr;
186  parameter );
187  }
188  }
189 } // namespace QgsWms
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::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
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.
Exception thrown in case of malformed request.
WMS parameter received from the client.
SERVER_EXPORT QString wmsServiceUrl(const QgsProject &project)
Returns the WMS service url defined in a QGIS project.
Median cut implementation.
void writeImage(QgsServerResponse &response, QImage &img, const QString &formatStr, int imageQuality)
Write image response.
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project)
Returns WMS service URL.
Definition: qgswmsutils.cpp:32
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:40
ImageOutputFormat parseImageFormat(const QString &format)
Parse image format parameter.
Definition: qgswmsutils.cpp:70