@@ -147,13 +147,30 @@ void QgsWMSServer::executeRequest()
147
147
}
148
148
149
149
// version
150
- QString version = mParameters .value ( " VERSION" , " 1.3.0" );
150
+ QString version = " 1.3.0" ;
151
+ if ( mParameters .contains ( " VERSION" ) )
152
+ {
153
+ version = mParameters .value ( " VERSION" );
154
+ }
155
+ else if ( mParameters .contains ( " WMTVER" ) ) // WMTVER needs to be supported by WMS 1.1.1 for backwards compatibility with WMS 1.0.0
156
+ {
157
+ version = mParameters .value ( " WMTVER" );
158
+ }
159
+
151
160
bool getProjectSettings = ( request.compare ( " GetProjectSettings" , Qt::CaseInsensitive ) == 0 );
152
161
if ( getProjectSettings )
153
162
{
154
163
version = " 1.3.0" ; // getProjectSettings extends WMS 1.3.0 capabilities
155
164
}
156
165
166
+ if ( version == " 1.1.1" )
167
+ {
168
+ if ( request.compare ( " capabilities" , Qt::CaseInsensitive ) == 0 )
169
+ {
170
+ request = QString ( " GetCapabilities" );
171
+ }
172
+ }
173
+
157
174
// GetCapabilities
158
175
if ( request.compare ( " GetCapabilities" , Qt::CaseInsensitive ) == 0 || getProjectSettings )
159
176
{
@@ -423,6 +440,12 @@ QDomDocument QgsWMSServer::getCapabilities( QString version, bool fullProjectInf
423
440
hrefString = serviceUrl ();
424
441
}
425
442
443
+ // href needs to be a prefix
444
+ if ( !hrefString.endsWith ( " ?" ) && !hrefString.endsWith ( " &" ) )
445
+ {
446
+ hrefString.append ( hrefString.contains ( " ?" ) ? " &" : " ?" );
447
+ }
448
+
426
449
if ( version == " 1.1.1" )
427
450
{
428
451
doc = QDomDocument ( " WMT_MS_Capabilities SYSTEM 'http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd'" ); // WMS 1.1.1 needs DOCTYPE "SYSTEM http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd"
@@ -556,7 +579,7 @@ QDomDocument QgsWMSServer::getCapabilities( QString version, bool fullProjectInf
556
579
557
580
// Exception element is mandatory
558
581
elem = doc.createElement ( " Exception" );
559
- appendFormats ( doc, elem, QStringList () << ( version == " 1.1.1" ? " application/vnd.ogc.se_xml" : " text/xml " ) );
582
+ appendFormats ( doc, elem, QStringList () << ( version == " 1.1.1" ? " application/vnd.ogc.se_xml" : " XML " ) );
560
583
capabilityElement.appendChild ( elem );
561
584
562
585
// UserDefinedSymbolization element
@@ -690,6 +713,10 @@ static QgsRectangle _parseBBOX( const QString &bboxStr, bool &ok )
690
713
}
691
714
692
715
ok = true ;
716
+ if ( d[2 ] <= d[0 ] || d[3 ] <= d[1 ] )
717
+ {
718
+ throw QgsMapServiceException ( " InvalidParameterValue" , " BBOX is empty" );
719
+ }
693
720
return QgsRectangle ( d[0 ], d[1 ], d[2 ], d[3 ] );
694
721
}
695
722
@@ -822,8 +849,6 @@ QImage* QgsWMSServer::getLegendGraphics()
822
849
}
823
850
QgsLayerTreeModel legendModel ( &rootGroup );
824
851
825
- QList<QgsLayerTreeNode*> rootChildren = rootGroup.children ();
826
-
827
852
if ( scaleDenominator > 0 )
828
853
legendModel.setLegendFilterByScale ( scaleDenominator );
829
854
@@ -863,7 +888,7 @@ QImage* QgsWMSServer::getLegendGraphics()
863
888
}
864
889
865
890
// find out DPI
866
- QImage* tmpImage = createImage ( 1 , 1 );
891
+ QImage* tmpImage = createImage ( 1 , 1 , false );
867
892
if ( !tmpImage )
868
893
return nullptr ;
869
894
qreal dpmm = tmpImage->dotsPerMeterX () / 1000.0 ;
@@ -893,7 +918,7 @@ QImage* QgsWMSServer::getLegendGraphics()
893
918
if ( !rule.isEmpty () )
894
919
{
895
920
// create second image with the right dimensions
896
- QImage* paintImage = createImage ( ruleSymbolWidth, ruleSymbolHeight );
921
+ QImage* paintImage = createImage ( ruleSymbolWidth, ruleSymbolHeight, false );
897
922
898
923
// go through the items a second time for painting
899
924
QPainter p ( paintImage );
@@ -915,6 +940,7 @@ QImage* QgsWMSServer::getLegendGraphics()
915
940
return paintImage;
916
941
}
917
942
943
+ QList<QgsLayerTreeNode*> rootChildren = rootGroup.children ();
918
944
Q_FOREACH ( QgsLayerTreeNode* node, rootChildren )
919
945
{
920
946
if ( QgsLayerTree::isLayer ( node ) )
@@ -954,7 +980,7 @@ QImage* QgsWMSServer::getLegendGraphics()
954
980
QSizeF minSize = legendRenderer.minimumSize ();
955
981
QSize s ( minSize.width () * dpmm, minSize.height () * dpmm );
956
982
957
- QImage* paintImage = createImage ( s.width (), s.height () );
983
+ QImage* paintImage = createImage ( s.width (), s.height (), false );
958
984
959
985
QPainter p ( paintImage );
960
986
p.setRenderHint ( QPainter::Antialiasing, true );
@@ -1449,6 +1475,17 @@ QImage* QgsWMSServer::getMap( HitTest* hitTest )
1449
1475
// theImage->save( QDir::tempPath() + QDir::separator() + "lastrender.png" );
1450
1476
// #endif
1451
1477
1478
+ thePainter.end ();
1479
+
1480
+ // test if width / height ratio of image is the same as the ratio of WIDTH / HEIGHT parameters. If not, the image has to be scaled (required by WMS spec)
1481
+ int widthParam = mParameters .value ( " WIDTH" , " 0" ).toInt ();
1482
+ int heightParam = mParameters .value ( " HEIGHT" , " 0" ).toInt ();
1483
+ if ( widthParam != theImage->width () || heightParam != theImage->height () )
1484
+ {
1485
+ // scale image
1486
+ *theImage = theImage->scaled ( widthParam, heightParam, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
1487
+ }
1488
+
1452
1489
return theImage;
1453
1490
}
1454
1491
@@ -1570,7 +1607,6 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result, const QString& version )
1570
1607
QgsRectangle mapExtent = mMapRenderer ->extent ();
1571
1608
double scaleDenominator = scaleCalc.calculate ( mapExtent, outputImage->width () );
1572
1609
mConfigParser ->setScaleDenominator ( scaleDenominator );
1573
- delete outputImage; // no longer needed for feature info
1574
1610
1575
1611
// read FEATURE_COUNT
1576
1612
int featureCount = 1 ;
@@ -1610,6 +1646,16 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result, const QString& version )
1610
1646
j = -1 ;
1611
1647
}
1612
1648
1649
+ // In case the output image is distorted (WIDTH/HEIGHT ratio not equal to BBOX width/height), I and J need to be adapted as well
1650
+ int widthParam = mParameters .value ( " WIDTH" , " -1" ).toInt ();
1651
+ int heightParam = mParameters .value ( " HEIGHT" , " -1" ).toInt ();
1652
+ if (( i != -1 && j != -1 && widthParam != -1 && heightParam != -1 ) && ( widthParam != outputImage->width () || heightParam != outputImage->height () ) )
1653
+ {
1654
+ i *= ( outputImage->width () / ( double )widthParam );
1655
+ j *= ( outputImage->height () / ( double )heightParam );
1656
+ }
1657
+ delete outputImage; // no longer needed for feature info
1658
+
1613
1659
// Normally, I/J or X/Y are mandatory parameters.
1614
1660
// However, in order to make attribute only queries via the FILTER parameter, it is allowed to skip them if the FILTER parameter is there
1615
1661
@@ -1926,7 +1972,7 @@ QImage* QgsWMSServer::initializeRendering( QStringList& layersList, QStringList&
1926
1972
return theImage;
1927
1973
}
1928
1974
1929
- QImage* QgsWMSServer::createImage ( int width, int height ) const
1975
+ QImage* QgsWMSServer::createImage ( int width, int height, bool useBbox ) const
1930
1976
{
1931
1977
bool conversionSuccess;
1932
1978
@@ -1946,6 +1992,32 @@ QImage* QgsWMSServer::createImage( int width, int height ) const
1946
1992
}
1947
1993
}
1948
1994
1995
+ // Adapt width / height if the aspect ratio does not correspond with the BBOX.
1996
+ // Required by WMS spec. 1.3.
1997
+ if ( useBbox )
1998
+ {
1999
+ bool bboxOk;
2000
+ QgsRectangle mapExtent = _parseBBOX ( mParameters .value ( " BBOX" ), bboxOk );
2001
+ if ( bboxOk )
2002
+ {
2003
+ double mapWidthHeightRatio = mapExtent.width () / mapExtent.height ();
2004
+ double imageWidthHeightRatio = ( double )width / ( double )height;
2005
+ if ( !qgsDoubleNear ( mapWidthHeightRatio, imageWidthHeightRatio, 0.0001 ) )
2006
+ {
2007
+ if ( mapWidthHeightRatio >= imageWidthHeightRatio )
2008
+ {
2009
+ // decrease image height
2010
+ height = width / mapWidthHeightRatio;
2011
+ }
2012
+ else
2013
+ {
2014
+ // decrease image width
2015
+ width = height * mapWidthHeightRatio;
2016
+ }
2017
+ }
2018
+ }
2019
+ }
2020
+
1949
2021
if ( width < 0 || height < 0 )
1950
2022
{
1951
2023
return nullptr ;
@@ -1962,6 +2034,19 @@ QImage* QgsWMSServer::createImage( int width, int height ) const
1962
2034
// transparent parameter
1963
2035
bool transparent = mParameters .value ( " TRANSPARENT" ).compare ( " true" , Qt::CaseInsensitive ) == 0 ;
1964
2036
2037
+ // background color
2038
+ QString bgColorString = mParameters .value ( " BGCOLOR" );
2039
+ if ( bgColorString.startsWith ( " 0x" , Qt::CaseInsensitive ) )
2040
+ {
2041
+ bgColorString.replace ( 0 , 2 , " #" );
2042
+ }
2043
+ QColor backgroundColor;
2044
+ backgroundColor.setNamedColor ( bgColorString );
2045
+ if ( !backgroundColor.isValid () )
2046
+ {
2047
+ backgroundColor = QColor ( Qt::white );
2048
+ }
2049
+
1965
2050
// use alpha channel only if necessary because it slows down performance
1966
2051
if ( transparent && !jpeg )
1967
2052
{
@@ -1971,7 +2056,7 @@ QImage* QgsWMSServer::createImage( int width, int height ) const
1971
2056
else
1972
2057
{
1973
2058
theImage = new QImage ( width, height, QImage::Format_RGB32 );
1974
- theImage->fill ( qRgb ( 255 , 255 , 255 ) );
2059
+ theImage->fill ( backgroundColor );
1975
2060
}
1976
2061
1977
2062
if ( !theImage )
@@ -2007,17 +2092,32 @@ int QgsWMSServer::configureMapRender( const QPaintDevice* paintDevice ) const
2007
2092
mMapRenderer ->setOutputSize ( QSize ( paintDevice->width (), paintDevice->height () ), paintDevice->logicalDpiX () );
2008
2093
2009
2094
// map extent
2010
- bool bboxOk;
2011
- QgsRectangle mapExtent = _parseBBOX ( mParameters .value ( " BBOX" , " 0,0,0,0" ), bboxOk );
2095
+ bool bboxOk = true ;
2096
+ QgsRectangle mapExtent;
2097
+ if ( mParameters .contains ( " BBOX" ) )
2098
+ {
2099
+ mapExtent = _parseBBOX ( mParameters .value ( " BBOX" , " 0,0,0,0" ), bboxOk );
2100
+ }
2101
+
2012
2102
if ( !bboxOk )
2013
2103
{
2014
2104
// throw a service exception
2015
2105
throw QgsMapServiceException ( " InvalidParameterValue" , " Invalid BBOX parameter" );
2016
2106
}
2017
2107
2108
+ if ( mParameters .contains ( " BBOX" ) && mapExtent.isEmpty () )
2109
+ {
2110
+ throw QgsMapServiceException ( " InvalidParameterValue" , " BBOX is empty" );
2111
+ }
2112
+
2018
2113
QGis::UnitType mapUnits = QGis::Degrees;
2019
2114
2020
2115
QString crs = mParameters .value ( " CRS" , mParameters .value ( " SRS" ) );
2116
+ if ( crs.compare ( " CRS:84" , Qt::CaseInsensitive ) == 0 )
2117
+ {
2118
+ crs = QString ( " EPSG:4326" );
2119
+ mapExtent.invert ();
2120
+ }
2021
2121
2022
2122
QgsCoordinateReferenceSystem outputCRS;
2023
2123
@@ -2154,6 +2254,12 @@ bool QgsWMSServer::infoPointToMapCoordinates( int i, int j, QgsPoint* infoPoint,
2154
2254
return false ;
2155
2255
}
2156
2256
2257
+ // check if i, j are in the pixel range of the image
2258
+ if ( i < 0 || i > mapRenderer->width () || j < 0 || j > mapRenderer->height () )
2259
+ {
2260
+ throw QgsMapServiceException ( " InvalidPoint" , " I/J parameters not within the pixel range" );
2261
+ }
2262
+
2157
2263
double xRes = mapRenderer->extent ().width () / mapRenderer->width ();
2158
2264
double yRes = mapRenderer->extent ().height () / mapRenderer->height ();
2159
2265
infoPoint->setX ( mapRenderer->extent ().xMinimum () + i * xRes + xRes / 2.0 );
0 commit comments