forked from hans/Nominatim
correctly handle single-point interpolations in reverse
Lookup in location_property_osmline needs to be special cased for startnumber = endnumber. Also adds tests for the case. Fixes #2680.
This commit is contained in:
@@ -64,7 +64,9 @@ class ReverseGeocode
|
|||||||
{
|
{
|
||||||
Debug::newFunction('lookupInterpolation');
|
Debug::newFunction('lookupInterpolation');
|
||||||
$sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,';
|
$sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,';
|
||||||
$sSQL .= ' (endnumber - startnumber) * ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fhnr,';
|
$sSQL .= ' (CASE WHEN endnumber != startnumber';
|
||||||
|
$sSQL .= ' THEN (endnumber - startnumber) * ST_LineLocatePoint(linegeo,'.$sPointSQL.')';
|
||||||
|
$sSQL .= ' ELSE startnumber END) as fhnr,';
|
||||||
$sSQL .= ' startnumber, endnumber, step,';
|
$sSQL .= ' startnumber, endnumber, step,';
|
||||||
$sSQL .= ' ST_Distance(linegeo,'.$sPointSQL.') as distance';
|
$sSQL .= ' ST_Distance(linegeo,'.$sPointSQL.') as distance';
|
||||||
$sSQL .= ' FROM location_property_osmline';
|
$sSQL .= ' FROM location_property_osmline';
|
||||||
|
|||||||
58
test/bdd/db/query/interpolation.feature
Normal file
58
test/bdd/db/query/interpolation.feature
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
@DB
|
||||||
|
Feature: Query of address interpolations
|
||||||
|
Tests that interpolated addresses can be queried correctly
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the grid
|
||||||
|
| 1 | | 2 | | 3 |
|
||||||
|
| 10 | | 12 | | 13 |
|
||||||
|
| 7 | | 8 | | 9 |
|
||||||
|
|
||||||
|
Scenario: Find interpolations with single number
|
||||||
|
Given the places
|
||||||
|
| osm | class | type | name | geometry |
|
||||||
|
| W10 | highway | primary | Nickway | 10,12,13 |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | addr+interpolation | geometry |
|
||||||
|
| W1 | place | houses | odd | 1,3 |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | housenr | geometry |
|
||||||
|
| N1 | place | house | 1 | 1 |
|
||||||
|
| N3 | place | house | 5 | 3 |
|
||||||
|
And the ways
|
||||||
|
| id | nodes |
|
||||||
|
| 1 | 1,3 |
|
||||||
|
When importing
|
||||||
|
When sending jsonv2 reverse point 2
|
||||||
|
Then results contain
|
||||||
|
| ID | display_name |
|
||||||
|
| 0 | 3, Nickway |
|
||||||
|
When sending search query "Nickway 3"
|
||||||
|
Then results contain
|
||||||
|
| osm | display_name |
|
||||||
|
| W1 | 3, Nickway |
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Find interpolations with multiple numbers
|
||||||
|
Given the places
|
||||||
|
| osm | class | type | name | geometry |
|
||||||
|
| W10 | highway | primary | Nickway | 10,12,13 |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | addr+interpolation | geometry |
|
||||||
|
| W1 | place | houses | even | 1,3 |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | housenr | geometry |
|
||||||
|
| N1 | place | house | 2 | 1 |
|
||||||
|
| N3 | place | house | 16 | 3 |
|
||||||
|
And the ways
|
||||||
|
| id | nodes |
|
||||||
|
| 1 | 1,3 |
|
||||||
|
When importing
|
||||||
|
When sending jsonv2 reverse point 2
|
||||||
|
Then results contain
|
||||||
|
| ID | display_name | centroid |
|
||||||
|
| 0 | 10, Nickway | 2 |
|
||||||
|
When sending search query "Nickway 10"
|
||||||
|
Then results contain
|
||||||
|
| osm | display_name | centroid |
|
||||||
|
| W1 | 10, Nickway | 2 |
|
||||||
@@ -62,6 +62,8 @@ class GenericResponse:
|
|||||||
|
|
||||||
if errorcode == 200 and fmt != 'debug':
|
if errorcode == 200 and fmt != 'debug':
|
||||||
getattr(self, '_parse_' + fmt)()
|
getattr(self, '_parse_' + fmt)()
|
||||||
|
else:
|
||||||
|
print("Bad response: ", page)
|
||||||
|
|
||||||
def _parse_json(self):
|
def _parse_json(self):
|
||||||
m = re.fullmatch(r'([\w$][^(]*)\((.*)\)', self.page)
|
m = re.fullmatch(r'([\w$][^(]*)\((.*)\)', self.page)
|
||||||
@@ -128,7 +130,7 @@ class GenericResponse:
|
|||||||
"\nBad value for row {} field '{}' in address. Expected: {}, got: {}.\nFull address: {}"""\
|
"\nBad value for row {} field '{}' in address. Expected: {}, got: {}.\nFull address: {}"""\
|
||||||
.format(idx, field, value, address[field], json.dumps(address, indent=4))
|
.format(idx, field, value, address[field], json.dumps(address, indent=4))
|
||||||
|
|
||||||
def match_row(self, row):
|
def match_row(self, row, context=None):
|
||||||
""" Match the result fields against the given behave table row.
|
""" Match the result fields against the given behave table row.
|
||||||
"""
|
"""
|
||||||
if 'ID' in row.headings:
|
if 'ID' in row.headings:
|
||||||
@@ -151,7 +153,12 @@ class GenericResponse:
|
|||||||
assert self.result[i]['osm_type'] in (OSM_TYPE[value[0]], value[0]), \
|
assert self.result[i]['osm_type'] in (OSM_TYPE[value[0]], value[0]), \
|
||||||
BadRowValueAssert(self, i, 'osm_type', value)
|
BadRowValueAssert(self, i, 'osm_type', value)
|
||||||
elif name == 'centroid':
|
elif name == 'centroid':
|
||||||
lon, lat = value.split(' ')
|
if ' ' in value:
|
||||||
|
lon, lat = value.split(' ')
|
||||||
|
elif context is not None:
|
||||||
|
lon, lat = context.osm.grid_node(int(value))
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Context needed when using grid coordinates")
|
||||||
self.assert_field(i, 'lat', float(lat))
|
self.assert_field(i, 'lat', float(lat))
|
||||||
self.assert_field(i, 'lon', float(lon))
|
self.assert_field(i, 'lon', float(lon))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -153,6 +153,20 @@ def website_reverse_request(context, fmt, lat, lon):
|
|||||||
|
|
||||||
context.response = ReverseResponse(outp, fmt or 'xml', status)
|
context.response = ReverseResponse(outp, fmt or 'xml', status)
|
||||||
|
|
||||||
|
@when(u'sending (?P<fmt>\S+ )?reverse point (?P<nodeid>.+)')
|
||||||
|
def website_reverse_request(context, fmt, nodeid):
|
||||||
|
params = {}
|
||||||
|
if fmt and fmt.strip() == 'debug':
|
||||||
|
params['debug'] = '1'
|
||||||
|
params['lon'], params['lat'] = (f'{c:f}' for c in context.osm.grid_node(int(nodeid)))
|
||||||
|
|
||||||
|
|
||||||
|
outp, status = send_api_query('reverse', params, fmt, context)
|
||||||
|
|
||||||
|
context.response = ReverseResponse(outp, fmt or 'xml', status)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@when(u'sending (?P<fmt>\S+ )?details query for (?P<query>.*)')
|
@when(u'sending (?P<fmt>\S+ )?details query for (?P<query>.*)')
|
||||||
def website_details_request(context, fmt, query):
|
def website_details_request(context, fmt, query):
|
||||||
params = {}
|
params = {}
|
||||||
@@ -238,7 +252,7 @@ def step_impl(context):
|
|||||||
context.execute_steps("then at least 1 result is returned")
|
context.execute_steps("then at least 1 result is returned")
|
||||||
|
|
||||||
for line in context.table:
|
for line in context.table:
|
||||||
context.response.match_row(line)
|
context.response.match_row(line, context=context)
|
||||||
|
|
||||||
@then(u'result (?P<lid>\d+ )?has (?P<neg>not )?attributes (?P<attrs>.*)')
|
@then(u'result (?P<lid>\d+ )?has (?P<neg>not )?attributes (?P<attrs>.*)')
|
||||||
def validate_attributes(context, lid, neg, attrs):
|
def validate_attributes(context, lid, neg, attrs):
|
||||||
|
|||||||
Reference in New Issue
Block a user