From b1f44af69f8dc936d89a9927dd987153c9b2e465 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 23 Jan 2025 14:18:49 +0100 Subject: [PATCH 1/2] [server][wms] GFI: fix sub-pixel tolerance Fix #59818 --- src/server/services/wms/qgswmsrenderer.cpp | 3 ++ .../test_qgsserver_wms_getfeatureinfo.py | 51 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index a7ddcc61a95..b663dd86b7a 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -3153,6 +3153,9 @@ namespace QgsWms } } + // Make sure the map unit tolerance is at least 1 pixel + mapUnitTolerance = std::max( mapUnitTolerance, 1.0 * rct.mapToPixel().mapUnitsPerPixel()); + QgsRectangle mapRectangle( infoPoint.x() - mapUnitTolerance, infoPoint.y() - mapUnitTolerance, infoPoint.x() + mapUnitTolerance, infoPoint.y() + mapUnitTolerance ); return ( mapSettings.mapToLayerCoordinates( ml, mapRectangle ) ); } diff --git a/tests/src/python/test_qgsserver_wms_getfeatureinfo.py b/tests/src/python/test_qgsserver_wms_getfeatureinfo.py index 080d85eda13..c5d4de2075e 100644 --- a/tests/src/python/test_qgsserver_wms_getfeatureinfo.py +++ b/tests/src/python/test_qgsserver_wms_getfeatureinfo.py @@ -37,9 +37,10 @@ from qgis.core import ( QgsMemoryProviderUtils, QgsProject, QgsWkbTypes, + QgsPointXY, ) -from qgis.PyQt.QtCore import QVariant -from qgis.server import QgsBufferServerRequest, QgsBufferServerResponse +from qgis.PyQt.QtCore import QVariant, QUrl +from qgis.server import QgsBufferServerRequest, QgsBufferServerResponse, QgsServer, QgsServerRequest from qgis.testing import unittest, QgisTestCase from test_qgsserver_wms import TestQgsServerWMSTestBase @@ -1357,6 +1358,52 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase): "test_project_values.qgz", ) + def test_getfeatureinfo_sub_px_tolerance(self): + + # create a memory layer with points + fields = QgsFields() + fields.append(QgsField('id', QVariant.Int)) + fields.append(QgsField('name', QVariant.String)) + layer = QgsMemoryProviderUtils.createMemoryLayer('points', fields, QgsWkbTypes.Point, QgsCoordinateReferenceSystem('EPSG:4326')) + + provider = layer.dataProvider() + self.assertTrue(layer.isValid()) + + # add some features + f = QgsFeature(fields) + f.setAttribute('id', 1) + f.setAttribute('name', 'point1') + f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(0, 0))) + provider.addFeature(f) + + f = QgsFeature(fields) + f.setAttribute('id', 2) + f.setAttribute('name', 'point2') + f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 1))) + provider.addFeature(f) + + f = QgsFeature(fields) + f.setAttribute('id', 2) + f.setAttribute('name', 'point2') + f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-1, -1))) + provider.addFeature(f) + + project = QgsProject() + project.addMapLayer(layer) + + # set up the WMS server + server = QgsServer() + request = QgsServerRequest() + # Choose a small extent to trigger the sub-pixel tolerance adjustment + w = 10 + w2 = int(w / 2) + request.setUrl(QUrl(f"?SERVICE=WMS&REQUEST=GetFeatureInfo&LAYERS=points&QUERY_LAYERS=points&INFO_FORMAT=application/json&FEATURE_COUNT=1&WIDTH={w}&HEIGHT={w}&CRS=EPSG:4326&STYLES=&BBOX=-1,-1,1,1&X={w2}&Y={w2}&VERSION=1.3.0")) + response = QgsBufferServerResponse() + + server.handleRequest(request, response, project) + body = response.body().data().decode("utf8").replace("\n", "") + self.assertEqual(len(json.loads(body)['features']), 1) + if __name__ == "__main__": unittest.main() From 348682ae0a25227928a7a1d2d49da3b0eaa9c9ec Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:22:57 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/server/services/wms/qgswmsrenderer.cpp | 2 +- .../test_qgsserver_wms_getfeatureinfo.py | 38 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index b663dd86b7a..317bc29e36a 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -3154,7 +3154,7 @@ namespace QgsWms } // Make sure the map unit tolerance is at least 1 pixel - mapUnitTolerance = std::max( mapUnitTolerance, 1.0 * rct.mapToPixel().mapUnitsPerPixel()); + mapUnitTolerance = std::max( mapUnitTolerance, 1.0 * rct.mapToPixel().mapUnitsPerPixel() ); QgsRectangle mapRectangle( infoPoint.x() - mapUnitTolerance, infoPoint.y() - mapUnitTolerance, infoPoint.x() + mapUnitTolerance, infoPoint.y() + mapUnitTolerance ); return ( mapSettings.mapToLayerCoordinates( ml, mapRectangle ) ); diff --git a/tests/src/python/test_qgsserver_wms_getfeatureinfo.py b/tests/src/python/test_qgsserver_wms_getfeatureinfo.py index c5d4de2075e..18040a8ca82 100644 --- a/tests/src/python/test_qgsserver_wms_getfeatureinfo.py +++ b/tests/src/python/test_qgsserver_wms_getfeatureinfo.py @@ -40,7 +40,12 @@ from qgis.core import ( QgsPointXY, ) from qgis.PyQt.QtCore import QVariant, QUrl -from qgis.server import QgsBufferServerRequest, QgsBufferServerResponse, QgsServer, QgsServerRequest +from qgis.server import ( + QgsBufferServerRequest, + QgsBufferServerResponse, + QgsServer, + QgsServerRequest, +) from qgis.testing import unittest, QgisTestCase from test_qgsserver_wms import TestQgsServerWMSTestBase @@ -1362,29 +1367,34 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase): # create a memory layer with points fields = QgsFields() - fields.append(QgsField('id', QVariant.Int)) - fields.append(QgsField('name', QVariant.String)) - layer = QgsMemoryProviderUtils.createMemoryLayer('points', fields, QgsWkbTypes.Point, QgsCoordinateReferenceSystem('EPSG:4326')) + fields.append(QgsField("id", QVariant.Int)) + fields.append(QgsField("name", QVariant.String)) + layer = QgsMemoryProviderUtils.createMemoryLayer( + "points", + fields, + QgsWkbTypes.Point, + QgsCoordinateReferenceSystem("EPSG:4326"), + ) provider = layer.dataProvider() self.assertTrue(layer.isValid()) # add some features f = QgsFeature(fields) - f.setAttribute('id', 1) - f.setAttribute('name', 'point1') + f.setAttribute("id", 1) + f.setAttribute("name", "point1") f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(0, 0))) provider.addFeature(f) f = QgsFeature(fields) - f.setAttribute('id', 2) - f.setAttribute('name', 'point2') + f.setAttribute("id", 2) + f.setAttribute("name", "point2") f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 1))) provider.addFeature(f) f = QgsFeature(fields) - f.setAttribute('id', 2) - f.setAttribute('name', 'point2') + f.setAttribute("id", 2) + f.setAttribute("name", "point2") f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-1, -1))) provider.addFeature(f) @@ -1397,12 +1407,16 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase): # Choose a small extent to trigger the sub-pixel tolerance adjustment w = 10 w2 = int(w / 2) - request.setUrl(QUrl(f"?SERVICE=WMS&REQUEST=GetFeatureInfo&LAYERS=points&QUERY_LAYERS=points&INFO_FORMAT=application/json&FEATURE_COUNT=1&WIDTH={w}&HEIGHT={w}&CRS=EPSG:4326&STYLES=&BBOX=-1,-1,1,1&X={w2}&Y={w2}&VERSION=1.3.0")) + request.setUrl( + QUrl( + f"?SERVICE=WMS&REQUEST=GetFeatureInfo&LAYERS=points&QUERY_LAYERS=points&INFO_FORMAT=application/json&FEATURE_COUNT=1&WIDTH={w}&HEIGHT={w}&CRS=EPSG:4326&STYLES=&BBOX=-1,-1,1,1&X={w2}&Y={w2}&VERSION=1.3.0" + ) + ) response = QgsBufferServerResponse() server.handleRequest(request, response, project) body = response.body().data().decode("utf8").replace("\n", "") - self.assertEqual(len(json.loads(body)['features']), 1) + self.assertEqual(len(json.loads(body)["features"]), 1) if __name__ == "__main__":