From 9a0db946c0b0012a504a92e34ffde3d0b2160055 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Thu, 1 Jul 2021 12:33:49 +0200
Subject: [PATCH 1/2] opengis: add tests on projections (#55284)

---
 tests/test_opengis.py | 147 ++++++++++++++++++++++++++++++++----------
 1 file changed, 113 insertions(+), 34 deletions(-)

diff --git a/tests/test_opengis.py b/tests/test_opengis.py
index 9b8f4e13..3a421aef 100644
--- a/tests/test_opengis.py
+++ b/tests/test_opengis.py
@@ -161,16 +161,33 @@ Was expecting one of:
     &amp;quot;*&amp;quot; ...
     &amp;quot;+&amp;quot; ...
     &amp;quot;-&amp;quot; ...
      Parsing : strEqualsIgnoreCase(nom_commune, &amp;apos;Grenoble&amp;apos;) = true AND strEqualsIgnoreCase(nom_voie, &amp;apos;rue albert recoura&amp;apos;) = true AND numero=8 BIS.</ows:ExceptionText>
   </ows:Exception>
 </ows:ExceptionReport>
 '''
 
+'''
+17, BOULEVARD EDOUARD REY 38000 Grenoble
+    1914059.51,  # 5.72465  (lat, wgs84)
+    4224699.2    # 45.18988 (lon, wgs84)
+
+2, place victor hugo 38000 Grenoble
+    1914042.47,  # 5.72434  (lat, wgs84)
+    4224665.2    # 45.18967 (lon, wgs84)
+
+28, BOULEVARD EDOUARD REY 38000 Grenoble
+    1914035.7,  # 45.18985  (lat, wgs84)
+    4224700.42  # 5.72427   (lon, wgs84)
+
+4, place victor hugo 38000 Grenoble
+    1914018.64, # 45.18947  (lat, wgs84)
+    4224644.61  # 5.72403   (lon, wgs84)
+'''
 FAKE_GEOLOCATED_FEATURE = '''{
     "crs": {
         "properties": {
             "name": "urn:ogc:def:crs:EPSG::3945"
         },
         "type": "name"
     },
     "features": [
@@ -349,38 +366,42 @@ def geoserver_geolocated_responses(url, **kwargs):
 
 
 def geoserver_circle_responses(url, **kwargs):
     if kwargs['params'].get('request') == 'GetCapabilities':
         return utils.FakedResponse(status_code=200, content=FAKE_SERVICE_CAPABILITIES)
     return utils.FakedResponse(status_code=200, content=json.dumps(FAKE_GEOLOCATED_FEATURE_CIRCLE))
 
 
+@pytest.mark.parametrize(
+    'projection, bbox',
+    [
+        ('EPSG:2154', '838570.184096,6523472.766191,838585.214059,6523495.321646'),
+        ('EPSG:3857', '532556.896735,5747844.261214,532579.160633,5747876.194333'),
+        ('EPSG:3945', '1838692.984807,4290077.106679,1838708.041015,4290099.673366'),
+        ('EPSG:4326', '45.796790,4.784040,45.796990,4.784240'),
+    ],
+)
 @mock.patch('passerelle.utils.Request.get')
-def test_feature_info(mocked_get, app, connector):
+def test_feature_info(mocked_get, app, connector, projection, bbox):
     endpoint = utils.generic_endpoint_url('opengis', 'feature_info', slug=connector.slug)
     assert endpoint == '/opengis/test/feature_info'
     mocked_get.return_value = utils.FakedResponse(content=FAKE_FEATURE_INFO, status_code=200)
+    connector.projection = projection
+    connector.save()
+
     resp = app.get(endpoint, params={'lat': '45.796890', 'lon': '4.784140'})
-    assert (
-        mocked_get.call_args[1]['params']['bbox']
-        == '532556.896735,5747844.261214,532579.160633,5747876.194333'
-    )
-    assert mocked_get.call_args[1]['params']['crs'] == 'EPSG:3857'
+    assert mocked_get.call_args[1]['params']['bbox'] == bbox
+    assert mocked_get.call_args[1]['params']['crs'] == projection
     assert (
         resp.json['data']['cad_cadastrecadparcelle_layer']['cad_cadastrecadparcelle_feature'][
             'natureproprietaire'
         ]
         == 'Particulier'
     )
-    connector.projection = 'EPSG:4326'
-    connector.save()
-    resp = app.get(endpoint, params={'lat': '45.796890', 'lon': '4.784140'})
-    assert mocked_get.call_args[1]['params']['bbox'] == '45.796790,4.784040,45.796990,4.784240'
-    assert mocked_get.call_args[1]['params']['crs'] == 'EPSG:4326'
 
 
 @mock.patch('passerelle.utils.Request.get')
 @pytest.mark.parametrize(
     'lat,lon',
     [
         ('bad-value', '4.784140'),
         ('45.796890', 'bad-value'),
@@ -390,32 +411,35 @@ def test_feature_info_bad_request(mocked_get, app, connector, lat, lon):
     endpoint = utils.generic_endpoint_url('opengis', 'feature_info', slug=connector.slug)
     assert endpoint == '/opengis/test/feature_info'
     mocked_get.return_value = utils.FakedResponse(content=FAKE_FEATURE_INFO, status_code=200)
     resp = app.get(endpoint, params={'lat': lat, 'lon': lon})
     assert resp.json['err'] == 1
     assert resp.json['err_desc'] == 'Bad coordinates format'
 
 
+@pytest.mark.parametrize(
+    'projection, bbox',
+    [
+        ('EPSG:2154', '843407.715537,6519055.898748,843824.642554,6519491.379063'),
+        ('EPSG:3857', '539339.671580,5741338.068556,539951.167806,5741949.564782'),
+        ('EPSG:3945', '1843532.148941,4285653.552517,1843949.695865,4286089.160315'),
+        ('EPSG:4326', '45.756026,4.844971,45.759859,4.850464'),
+    ],
+)
 @mock.patch('passerelle.utils.Request.get')
-def test_tile(mocked_get, app, connector):
+def test_tile(mocked_get, app, connector, projection, bbox):
     endpoint = utils.generic_endpoint_url('opengis', 'tile', slug=connector.slug)
     assert endpoint == '/opengis/test/tile'
     mocked_get.return_value = utils.FakedResponse(content=b'\x89PNG\r\n\x1a\n\x00\x00...', status_code=200)
-    resp = app.get(endpoint + '/16/33650/23378.png')
-    assert mocked_get.call_args[1]['params']['crs'] == 'EPSG:3857'
-    assert (
-        mocked_get.call_args[1]['params']['bbox']
-        == '539339.671580,5741338.068556,539951.167806,5741949.564782'
-    )
-    connector.projection = 'EPSG:4326'
+    connector.projection = projection
     connector.save()
     resp = app.get(endpoint + '/16/33650/23378.png')
-    assert mocked_get.call_args[1]['params']['crs'] == 'EPSG:4326'
-    assert mocked_get.call_args[1]['params']['bbox'] == '45.756026,4.844971,45.759859,4.850464'
+    assert mocked_get.call_args[1]['params']['crs'] == projection
+    assert mocked_get.call_args[1]['params']['bbox'] == bbox
     assert resp.content == b'\x89PNG\r\n\x1a\n\x00\x00...'
 
 
 @mock.patch('passerelle.utils.Request.get')
 def test_get_feature_with_no_wfs_url(mocked_get, app, connector):
     connector.wfs_service_url = ''
     connector.save()
     endpoint = utils.generic_endpoint_url('opengis', 'features', slug=connector.slug)
@@ -565,42 +589,30 @@ def test_reverse_geocoding(mocked_get, app, connector):
     assert resp.json['lat'] == '45.189397'
     assert resp.json['address']['house_number'] == '4'
     assert resp.json['address']['road'] == 'place victor hugo'
     assert resp.json['address']['postcode'] == '38000'
     assert resp.json['address']['city'] == 'Grenoble'
 
 
 @mock.patch('passerelle.utils.Request.get')
-def test_reverse_geocoding_closest(mocked_get, app, connector):
+def test_reverse_geocoding_error(mocked_get, app, connector):
     connector.search_radius = 45
     connector.projection = 'EPSG:3945'
     connector.save()
     endpoint = utils.generic_endpoint_url('opengis', 'reverse', slug=connector.slug)
     assert endpoint == '/opengis/test/reverse'
 
     def side_effect(url, **kwargs):
         if kwargs['params'].get('request') == 'GetCapabilities':
             return utils.FakedResponse(status_code=200, content=FAKE_SERVICE_CAPABILITIES)
         return mock.DEFAULT
 
     mocked_get.side_effect = side_effect
     mocked_get.return_value = utils.FakedResponse(content=FAKE_GEOLOCATED_FEATURE, status_code=200)
-    resp = app.get(endpoint, params={'lat': '45.1893469606986', 'lon': '5.72462060798'})
-    assert (
-        mocked_get.call_args[1]['params']['cql_filter']
-        == 'DWITHIN(the_geom,Point(1914061.486036 4224640.457791),45,meters)'
-    )
-    assert resp.json['lon'] == '5.724077'
-    assert resp.json['lat'] == '45.189397'
-    assert resp.json['address']['house_number'] == '4'
-    assert resp.json['address']['road'] == 'place victor hugo'
-    assert resp.json['address']['postcode'] == '38000'
-    assert resp.json['address']['city'] == 'Grenoble'
-
     resp = app.get(
         endpoint,
         params={
             'lat': '45.1893469606986',
             'lon': '5.72462060798',
             'addressdetails': 'details',
             'accept-language': 'fr',
         },
@@ -620,16 +632,83 @@ def test_reverse_geocoding_closest(mocked_get, app, connector):
     assert resp.json['err_desc'] == 'Unable to geocode'
 
     mocked_get.return_value = utils.FakedResponse(status_code=404, content='{}', ok=False)
     resp = app.get(endpoint, params={'lat': '45.183784', 'lon': '5.714885'})
     assert resp.json['err'] == 1
     assert resp.json['err_desc'] == 'Webservice returned status code 404'
 
 
+@pytest.mark.parametrize(
+    'projection, x, y, lon, lat, house_number, road',
+    [
+        (
+            'EPSG:2154',
+            '913930.322365',
+            '6458139.491904',
+            '14.292822',
+            '24.779874',
+            '28',
+            'boulevard édouard rey',
+        ),
+        (
+            'EPSG:3857',
+            '637261.851065',
+            '5651379.708172',
+            '17.194075',
+            '35.446317',
+            '28',
+            'boulevard édouard rey',
+        ),
+        ('EPSG:3945', '1914061.486036', '4224640.457791', '5.724077', '45.189397', '4', 'place victor hugo'),
+        ('EPSG:4326', '5.724621', '45.189347', '1914018.640000', '4224644.610000', '4', 'place victor hugo'),
+    ],
+)
+@pytest.mark.parametrize('search_radius', [45, 10])
+@mock.patch('passerelle.utils.Request.get')
+def test_reverse_geocoding_closest(
+    mocked_get, app, connector, search_radius, projection, x, y, lon, lat, house_number, road
+):
+    connector.search_radius = search_radius
+    connector.projection = projection
+    connector.save()
+    endpoint = utils.generic_endpoint_url('opengis', 'reverse', slug=connector.slug)
+    assert endpoint == '/opengis/test/reverse'
+
+    def side_effect(url, **kwargs):
+        if kwargs['params'].get('request') == 'GetCapabilities':
+            return utils.FakedResponse(status_code=200, content=FAKE_SERVICE_CAPABILITIES)
+        return mock.DEFAULT
+
+    # adapt locations to selected projection
+    fake_geolocated_feature = json.loads(FAKE_GEOLOCATED_FEATURE)
+    # fake_geolocated_feature['crs']['properties']['name'] = "urn:ogc:def:crs:EPSG::3945"
+    for feature in fake_geolocated_feature['features']:
+        wgs84_lon, wgs84_lat = feature['geometry']['coordinates']
+        proj_lon, proj_lat = connector.convert_coordinates(wgs84_lon, wgs84_lat, reverse=True)
+        feature['geometry']['coordinates'] = [proj_lon, proj_lat]
+        fake_geolocated_feature = json.dumps(fake_geolocated_feature)
+
+    req_lon, req_lat = connector.convert_coordinates('5.72462060798', '45.1893469606986', reverse=True)
+    mocked_get.side_effect = side_effect
+    mocked_get.return_value = utils.FakedResponse(content=FAKE_GEOLOCATED_FEATURE, status_code=200)
+    resp = app.get(endpoint, params={'lat': '45.1893469606986', 'lon': '5.72462060798'})
+    assert mocked_get.call_args[1]['params']['cql_filter'] == 'DWITHIN(the_geom,Point(%s %s),%s,meters)' % (
+        x,
+        y,
+        search_radius,
+    )
+    assert resp.json['lon'] == lon
+    assert resp.json['lat'] == lat
+    assert resp.json['address']['house_number'] == house_number
+    assert resp.json['address']['road'] == road
+    assert resp.json['address']['postcode'] == '38000'
+    assert resp.json['address']['city'] == 'Grenoble'
+
+
 @mock.patch('passerelle.utils.Request.get')
 def test_opengis_query_cache_update(mocked_get, app, connector, query):
     mocked_get.side_effect = geoserver_geolocated_responses
     query.update_cache()
 
     assert mocked_get.call_args[1]['params']['filter'] == query.filter_expression
     assert mocked_get.call_args[1]['params']['typenames'] == query.typename
     assert FeatureCache.objects.count() == 4
-- 
2.30.2

