Skip to content

Commit 35c4209

Browse files
committedJun 4, 2019
New parameter for WMS service: tile_buffer [needs-docs][FEATURE]
1 parent 8b83a46 commit 35c4209

File tree

16 files changed

+817
-30
lines changed

16 files changed

+817
-30
lines changed
 

‎python/server/auto_generated/qgsserverprojectutils.sip.in

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ Returns the quality for WMS images defined in a QGIS project.
152152
:param project: the QGIS project
153153

154154
:return: quality if defined in project, -1 otherwise.
155+
%End
156+
157+
int wmsTileBuffer( const QgsProject &project );
158+
%Docstring
159+
Returns the tile buffer for WMS images defined in a QGIS project.
160+
161+
:param project: the QGIS project
162+
163+
:return: tile buffer if defined in project, 0 otherwise.
164+
165+
.. versionadded:: 3.10
155166
%End
156167

157168
int wmsMaxAtlasFeatures( const QgsProject &project );

‎src/app/qgsprojectproperties.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,9 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
644644
mWMSImageQualitySpinBox->setValue( imageQuality );
645645
}
646646

647+
// WMS tileBuffer
648+
mWMSTileBufferSpinBox->setValue( QgsProject::instance()->readNumEntry( QStringLiteral( "WMSTileBuffer" ), QStringLiteral( "/" ), 0 ) );
649+
647650
mWMSMaxAtlasFeaturesSpinBox->setValue( QgsProject::instance()->readNumEntry( QStringLiteral( "WMSMaxAtlasFeatures" ), QStringLiteral( "/" ), 1 ) );
648651

649652
QString defaultValueToolTip = tr( "In case of no other information to evaluate the map unit sized symbols, it uses default scale (on projected CRS) or default map units per mm (on geographic CRS)." );
@@ -1323,6 +1326,9 @@ void QgsProjectProperties::apply()
13231326
QgsProject::instance()->writeEntry( QStringLiteral( "WMSImageQuality" ), QStringLiteral( "/" ), imageQualityValue );
13241327
}
13251328

1329+
// WMS TileBuffer
1330+
QgsProject::instance()->writeEntry( QStringLiteral( "WMSTileBuffer" ), QStringLiteral( "/" ), mWMSTileBufferSpinBox->value() );
1331+
13261332
int maxAtlasFeatures = mWMSMaxAtlasFeaturesSpinBox->value();
13271333
QgsProject::instance()->writeEntry( QStringLiteral( "WMSMaxAtlasFeatures" ), QStringLiteral( "/" ), maxAtlasFeatures );
13281334

‎src/server/qgsserverprojectutils.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ int QgsServerProjectUtils::wmsImageQuality( const QgsProject &project )
111111
return project.readNumEntry( QStringLiteral( "WMSImageQuality" ), QStringLiteral( "/" ), -1 );
112112
}
113113

114+
int QgsServerProjectUtils::wmsTileBuffer( const QgsProject &project )
115+
{
116+
return project.readNumEntry( QStringLiteral( "WMSTileBuffer" ), QStringLiteral( "/" ), 0 );
117+
}
118+
114119
int QgsServerProjectUtils::wmsMaxAtlasFeatures( const QgsProject &project )
115120
{
116121
return project.readNumEntry( QStringLiteral( "WMSMaxAtlasFeatures" ), QStringLiteral( "/" ), 1 );

‎src/server/qgsserverprojectutils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ namespace QgsServerProjectUtils
147147
*/
148148
SERVER_EXPORT int wmsImageQuality( const QgsProject &project );
149149

150+
/**
151+
* Returns the tile buffer for WMS images defined in a QGIS project.
152+
* \param project the QGIS project
153+
* \returns tile buffer if defined in project, 0 otherwise.
154+
* \since QGIS 3.10
155+
*/
156+
SERVER_EXPORT int wmsTileBuffer( const QgsProject &project );
157+
150158
/**
151159
* Returns the maximum number of atlas features which can be printed in a request
152160
* \param project the QGIS project

‎src/server/services/wms/qgswmsgetmap.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace QgsWms
4444
context.setFlag( QgsWmsRenderContext::AddHighlightLayers );
4545
context.setFlag( QgsWmsRenderContext::AddExternalLayers );
4646
context.setFlag( QgsWmsRenderContext::SetAccessControl );
47+
context.setFlag( QgsWmsRenderContext::UseTileBuffer );
4748
context.setParameters( parameters );
4849

4950
// rendering

‎src/server/services/wms/qgswmsparameters.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ namespace QgsWms
223223
QVariant( 0 ) );
224224
save( pQuality );
225225

226+
const QgsWmsParameter pTiled( QgsWmsParameter::TILED,
227+
QVariant::Bool,
228+
QVariant( false ) );
229+
save( pTiled );
230+
226231
const QgsWmsParameter pBoxSpace( QgsWmsParameter::BOXSPACE,
227232
QVariant::Double,
228233
QVariant( 2.0 ) );
@@ -944,6 +949,16 @@ namespace QgsWms
944949
return mWmsParameters[ QgsWmsParameter::IMAGE_QUALITY ].toInt();
945950
}
946951

952+
QString QgsWmsParameters::tiled() const
953+
{
954+
return mWmsParameters[ QgsWmsParameter::TILED ].toString();
955+
}
956+
957+
bool QgsWmsParameters::tiledAsBool() const
958+
{
959+
return mWmsParameters[ QgsWmsParameter::TILED ].toBool();
960+
}
961+
947962
QString QgsWmsParameters::showFeatureCount() const
948963
{
949964
return mWmsParameters[ QgsWmsParameter::SHOWFEATURECOUNT ].toString();

‎src/server/services/wms/qgswmsparameters.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ namespace QgsWms
177177
ATLAS_PK,
178178
FORMAT_OPTIONS,
179179
SRCWIDTH,
180-
SRCHEIGHT
180+
SRCHEIGHT,
181+
TILED
181182
};
182183
Q_ENUM( Name )
183184

@@ -628,6 +629,20 @@ namespace QgsWms
628629
*/
629630
int imageQualityAsInt() const;
630631

632+
/**
633+
* Returns TILED parameter or an empty string if not
634+
* defined.
635+
* \since QGIS 3.10
636+
*/
637+
QString tiled() const;
638+
639+
/**
640+
* Returns TILED parameter as a boolean.
641+
* \throws QgsBadRequestException
642+
* \since QGIS 3.10
643+
*/
644+
bool tiledAsBool() const;
645+
631646
/**
632647
* Returns infoFormat. If the INFO_FORMAT parameter is not used, then the
633648
* default value is text/plain.

‎src/server/services/wms/qgswmsrendercontext.cpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
using namespace QgsWms;
2525

2626
const double OGC_PX_M = 0.00028; // OGC reference pixel size in meter
27-
2827
QgsWmsRenderContext::QgsWmsRenderContext( const QgsProject *project, QgsServerInterface *interface )
2928
: mProject( project )
3029
, mInterface( interface )
@@ -132,6 +131,17 @@ int QgsWmsRenderContext::imageQuality() const
132131
return imageQuality;
133132
}
134133

134+
int QgsWmsRenderContext::tileBuffer() const
135+
{
136+
int tileBuffer = 0;
137+
138+
if ( mParameters.tiledAsBool() )
139+
{
140+
tileBuffer = QgsServerProjectUtils::wmsTileBuffer( *mProject );
141+
}
142+
return tileBuffer;
143+
}
144+
135145
int QgsWmsRenderContext::precision() const
136146
{
137147
int precision = QgsServerProjectUtils::wmsFeatureInfoPrecision( *mProject );
@@ -638,22 +648,40 @@ bool QgsWmsRenderContext::isValidWidthHeight() const
638648
return true;
639649
}
640650

651+
QgsRectangle QgsWmsRenderContext::mapExtent() const
652+
{
653+
QgsRectangle extent = mParameters.bboxAsRectangle();
654+
if ( !mParameters.bbox().isEmpty() && extent.isEmpty() )
655+
{
656+
throw QgsBadRequestException( QgsServiceException::QGIS_InvalidParameterValue,
657+
mParameters[QgsWmsParameter::BBOX] );
658+
}
659+
const double extentWidth = extent.width();
660+
const double extentHeight = extent.height();
661+
const int width = mapWidth();
662+
const int height = mapHeight();
663+
const double resolutionX = extentWidth / width;
664+
const double resolutionY = extentHeight / height;
665+
const int tBuffer = mFlags & UseTileBuffer ? tileBuffer() : 0;
666+
const double xMin = extent.xMinimum() - tBuffer * resolutionX;
667+
const double yMin = extent.yMinimum() - tBuffer * resolutionY;
668+
const double xMax = extent.xMaximum() + tBuffer * resolutionX;
669+
const double yMax = extent.yMaximum() + tBuffer * resolutionY;
670+
return QgsRectangle( xMin, yMin, xMax, yMax );
671+
}
672+
641673
QSize QgsWmsRenderContext::mapSize( const bool aspectRatio ) const
642674
{
643-
int width = mapWidth();
644-
int height = mapHeight();
675+
int tBuffer = mFlags & UseTileBuffer ? tileBuffer() : 0;
676+
int width = mapWidth() + tBuffer * 2;
677+
int height = mapHeight() + tBuffer * 2;
645678

646679
// Adapt width / height if the aspect ratio does not correspond with the BBOX.
647680
// Required by WMS spec. 1.3.
648681
if ( aspectRatio
649682
&& mParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) )
650683
{
651-
QgsRectangle extent = mParameters.bboxAsRectangle();
652-
if ( !mParameters.bbox().isEmpty() && extent.isEmpty() )
653-
{
654-
throw QgsBadRequestException( QgsServiceException::QGIS_InvalidParameterValue,
655-
mParameters[QgsWmsParameter::BBOX] );
656-
}
684+
QgsRectangle extent = mapExtent();
657685

658686
QString crs = mParameters.crs();
659687
if ( crs.compare( "CRS:84", Qt::CaseInsensitive ) == 0 )

‎src/server/services/wms/qgswmsrendercontext.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ namespace QgsWms
4747
AddQueryLayers = 0x80,
4848
UseWfsLayersOnly = 0x100,
4949
AddExternalLayers = 0x200,
50-
UseSrcWidthHeight = 0x400
50+
UseSrcWidthHeight = 0x400,
51+
UseTileBuffer = 0x800
5152
};
5253
Q_DECLARE_FLAGS( Flags, Flag )
5354

@@ -148,6 +149,13 @@ namespace QgsWms
148149
*/
149150
int imageQuality() const;
150151

152+
/**
153+
* Returns the tile buffer value to use for rendering according to the
154+
* current configuration.
155+
* \since QGIS 3.10
156+
*/
157+
int tileBuffer() const;
158+
151159
/**
152160
* Returns the precision to use according to the current configuration.
153161
*/
@@ -200,6 +208,12 @@ namespace QgsWms
200208
*/
201209
QMap<QString, QList<QgsMapLayer *> > layerGroups() const;
202210

211+
/**
212+
* Returns the extent of the map to render.
213+
* \since QGIS 3.10
214+
*/
215+
QgsRectangle mapExtent() const;
216+
203217
/**
204218
* Returns the size (in pixels) of the map to render, according to width
205219
* and height WMS parameters as well as the \a aspectRatio option.

‎src/server/services/wms/qgswmsrenderer.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
* *
1818
***************************************************************************/
1919

20-
2120
#include "qgswmsutils.h"
2221
#include "qgsjsonutils.h"
2322
#include "qgswmsrenderer.h"
@@ -771,6 +770,12 @@ namespace QgsWms
771770
// painting is terminated
772771
painter->end();
773772

773+
if ( mContext.testFlag( QgsWmsRenderContext::UseTileBuffer ) )
774+
{
775+
QRect rect( mContext.tileBuffer(), mContext.tileBuffer(), mContext.mapWidth(), mContext.mapHeight() );
776+
image.reset( new QImage( image.get()->copy( rect ) ) );
777+
}
778+
774779
// scale output image if necessary (required by WMS spec)
775780
QImage *scaledImage = scaleImage( image.get() );
776781
if ( scaledImage )
@@ -977,12 +982,7 @@ namespace QgsWms
977982
mapSettings.setOutputDpi( paintDevice->logicalDpiX() );
978983

979984
//map extent
980-
QgsRectangle mapExtent = mWmsParameters.bboxAsRectangle();
981-
if ( !mWmsParameters.bbox().isEmpty() && mapExtent.isEmpty() )
982-
{
983-
throw QgsBadRequestException( QgsServiceException::QGIS_InvalidParameterValue,
984-
mWmsParameters[QgsWmsParameter::BBOX] );
985-
}
985+
QgsRectangle mapExtent = mContext.mapExtent();
986986

987987
QString crs = mWmsParameters.crs();
988988
if ( crs.compare( "CRS:84", Qt::CaseInsensitive ) == 0 )

‎src/ui/qgsprojectpropertiesbase.ui

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@
236236
</sizepolicy>
237237
</property>
238238
<property name="currentIndex">
239-
<number>4</number>
239+
<number>8</number>
240240
</property>
241241
<widget class="QWidget" name="mProjOptsGeneral">
242242
<layout class="QVBoxLayout" name="verticalLayout_6">
@@ -265,8 +265,8 @@
265265
<rect>
266266
<x>0</x>
267267
<y>0</y>
268-
<width>537</width>
269-
<height>795</height>
268+
<width>535</width>
269+
<height>740</height>
270270
</rect>
271271
</property>
272272
<layout class="QVBoxLayout" name="verticalLayout_8">
@@ -863,8 +863,8 @@
863863
<rect>
864864
<x>0</x>
865865
<y>0</y>
866-
<width>546</width>
867-
<height>164</height>
866+
<width>544</width>
867+
<height>155</height>
868868
</rect>
869869
</property>
870870
<layout class="QVBoxLayout" name="verticalLayout_7">
@@ -938,8 +938,8 @@
938938
<rect>
939939
<x>0</x>
940940
<y>0</y>
941-
<width>269</width>
942-
<height>553</height>
941+
<width>266</width>
942+
<height>524</height>
943943
</rect>
944944
</property>
945945
<layout class="QVBoxLayout" name="verticalLayout_12">
@@ -1514,8 +1514,8 @@
15141514
<rect>
15151515
<x>0</x>
15161516
<y>0</y>
1517-
<width>167</width>
1518-
<height>55</height>
1517+
<width>165</width>
1518+
<height>52</height>
15191519
</rect>
15201520
</property>
15211521
<layout class="QVBoxLayout" name="verticalLayout_17">
@@ -1575,9 +1575,9 @@
15751575
<property name="geometry">
15761576
<rect>
15771577
<x>0</x>
1578-
<y>0</y>
1579-
<width>598</width>
1580-
<height>2732</height>
1578+
<y>-793</y>
1579+
<width>671</width>
1580+
<height>2614</height>
15811581
</rect>
15821582
</property>
15831583
<layout class="QVBoxLayout" name="verticalLayout_13">
@@ -2437,6 +2437,20 @@
24372437
</item>
24382438
</layout>
24392439
</item>
2440+
<item row="11" column="0" colspan="3">
2441+
<layout class="QHBoxLayout" name="horizontalLayout_18">
2442+
<item>
2443+
<widget class="QLabel" name="label_33">
2444+
<property name="text">
2445+
<string>Tile buffer</string>
2446+
</property>
2447+
</widget>
2448+
</item>
2449+
<item>
2450+
<widget class="QSpinBox" name="mWMSTileBufferSpinBox"/>
2451+
</item>
2452+
</layout>
2453+
</item>
24402454
</layout>
24412455
</widget>
24422456
</item>

‎tests/src/python/test_qgsserver_wms_getmap.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,63 @@ def test_wms_getmap_root_custom_layer_order_regression_21917(self):
15781578
r, h = self._result(self._execute_request(qs))
15791579
self._img_diff_error(r, h, "WMS_GetMap_Group_Layer_Order")
15801580

1581+
def test_wms_getmap_tile_buffer(self):
1582+
"""Test the implementation of tile_map_edge_buffer from mapserver."""
1583+
1584+
# Check without tiled parameters (default is false)
1585+
qs = "?" + "&".join(["%s=%s" % i for i in list({
1586+
"MAP": urllib.parse.quote(os.path.join(self.testdata_path, 'wms_tile_buffer.qgs')),
1587+
"SERVICE": "WMS",
1588+
"VERSION": "1.3.0",
1589+
"REQUEST": "GetMap",
1590+
"BBOX": "310187,6163153,324347,6177313",
1591+
"CRS": "EPSG:3857",
1592+
"WIDTH": "512",
1593+
"HEIGHT": "512",
1594+
"LAYERS": "wms_tile_buffer_data",
1595+
"FORMAT": "image/png",
1596+
"TILED": "false"
1597+
}.items())])
1598+
1599+
r, h = self._result(self._execute_request(qs))
1600+
self._img_diff_error(r, h, "WMS_GetMap_Tiled_False")
1601+
1602+
# Check with tiled=false
1603+
qs = "?" + "&".join(["%s=%s" % i for i in list({
1604+
"MAP": urllib.parse.quote(os.path.join(self.testdata_path, 'wms_tile_buffer.qgs')),
1605+
"SERVICE": "WMS",
1606+
"VERSION": "1.3.0",
1607+
"REQUEST": "GetMap",
1608+
"BBOX": "310187,6163153,324347,6177313",
1609+
"CRS": "EPSG:3857",
1610+
"WIDTH": "512",
1611+
"HEIGHT": "512",
1612+
"LAYERS": "wms_tile_buffer_data",
1613+
"FORMAT": "image/png",
1614+
"TILED": "false"
1615+
}.items())])
1616+
1617+
r, h = self._result(self._execute_request(qs))
1618+
self._img_diff_error(r, h, "WMS_GetMap_Tiled_False")
1619+
1620+
# Check with tiled=true
1621+
qs = "?" + "&".join(["%s=%s" % i for i in list({
1622+
"MAP": urllib.parse.quote(os.path.join(self.testdata_path, 'wms_tile_buffer.qgs')),
1623+
"SERVICE": "WMS",
1624+
"VERSION": "1.3.0",
1625+
"REQUEST": "GetMap",
1626+
"BBOX": "310187,6163153,324347,6177313",
1627+
"CRS": "EPSG:3857",
1628+
"WIDTH": "512",
1629+
"HEIGHT": "512",
1630+
"LAYERS": "wms_tile_buffer_data",
1631+
"FORMAT": "image/png",
1632+
"TILED": "true"
1633+
}.items())])
1634+
1635+
r, h = self._result(self._execute_request(qs))
1636+
self._img_diff_error(r, h, "WMS_GetMap_Tiled_True")
1637+
15811638

15821639
if __name__ == '__main__':
15831640
unittest.main()
Loading
Loading

‎tests/testdata/qgis_server/wms_tile_buffer.qgs

Lines changed: 613 additions & 0 deletions
Large diffs are not rendered by default.
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.