mirror of
https://github.com/osm-search/Nominatim.git
synced 2024-10-27 11:42:46 +03:00
make localisation of results explicit
Localisation was previously done as part of the formatting but might also be useful on its own when working with the results directly.
This commit is contained in:
parent
dcfb228c9a
commit
f335e78d1e
@ -103,6 +103,9 @@ class BaseResult:
|
||||
place_id : Optional[int] = None
|
||||
osm_object: Optional[Tuple[str, int]] = None
|
||||
|
||||
locale_name: Optional[str] = None
|
||||
display_name: Optional[str] = None
|
||||
|
||||
names: Optional[Dict[str, str]] = None
|
||||
address: Optional[Dict[str, str]] = None
|
||||
extratags: Optional[Dict[str, str]] = None
|
||||
@ -147,6 +150,18 @@ class BaseResult:
|
||||
return self.importance or (0.7500001 - (self.rank_search/40.0))
|
||||
|
||||
|
||||
def localize(self, locales: Locales) -> None:
|
||||
""" Fill the locale_name and the display_name field for the
|
||||
place and, if available, its address information.
|
||||
"""
|
||||
self.locale_name = locales.display_name(self.names)
|
||||
if self.address_rows:
|
||||
self.display_name = ', '.join(self.address_rows.localize(locales))
|
||||
else:
|
||||
self.display_name = self.locale_name
|
||||
|
||||
|
||||
|
||||
BaseResultT = TypeVar('BaseResultT', bound=BaseResult)
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
@ -111,16 +111,16 @@ def _format_details_json(result: napi.DetailedResult, options: Mapping[str, Any]
|
||||
out.keyval('category', result.category[0])\
|
||||
.keyval('type', result.category[1])\
|
||||
.keyval('admin_level', result.admin_level)\
|
||||
.keyval('localname', locales.display_name(result.names))\
|
||||
.keyval_not_none('names', result.names or None)\
|
||||
.keyval_not_none('addresstags', result.address or None)\
|
||||
.keyval('localname', result.locale_name or '')\
|
||||
.keyval('names', result.names or {})\
|
||||
.keyval('addresstags', result.address or {})\
|
||||
.keyval_not_none('housenumber', result.housenumber)\
|
||||
.keyval_not_none('calculated_postcode', result.postcode)\
|
||||
.keyval_not_none('country_code', result.country_code)\
|
||||
.keyval_not_none('indexed_date', result.indexed_date, lambda v: v.isoformat())\
|
||||
.keyval_not_none('importance', result.importance)\
|
||||
.keyval('calculated_importance', result.calculated_importance())\
|
||||
.keyval_not_none('extratags', result.extratags or None)\
|
||||
.keyval('extratags', result.extratags or {})\
|
||||
.keyval_not_none('calculated_wikipedia', result.wikipedia)\
|
||||
.keyval('rank_address', result.rank_address)\
|
||||
.keyval('rank_search', result.rank_search)\
|
||||
|
@ -68,8 +68,6 @@ def format_base_json(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
class_label: str) -> str:
|
||||
""" Return the result list as a simple json string in custom Nominatim format.
|
||||
"""
|
||||
locales = options.get('locales', napi.Locales())
|
||||
|
||||
out = JsonWriter()
|
||||
|
||||
if simple:
|
||||
@ -79,8 +77,6 @@ def format_base_json(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
out.start_array()
|
||||
|
||||
for result in results:
|
||||
label_parts = result.address_rows.localize(locales) if result.address_rows else []
|
||||
|
||||
out.start_object()\
|
||||
.keyval_not_none('place_id', result.place_id)\
|
||||
.keyval('licence', cl.OSM_ATTRIBUTION)\
|
||||
@ -96,8 +92,8 @@ def format_base_json(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
.keyval('addresstype', cl.get_label_tag(result.category, result.extratags,
|
||||
result.rank_address,
|
||||
result.country_code))\
|
||||
.keyval('name', locales.display_name(result.names))\
|
||||
.keyval('display_name', ', '.join(label_parts))
|
||||
.keyval('name', result.locale_name or '')\
|
||||
.keyval('display_name', result.display_name or '')
|
||||
|
||||
|
||||
if options.get('icon_base_url', None):
|
||||
@ -151,8 +147,6 @@ def format_base_geojson(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
if not results and simple:
|
||||
return '{"error":"Unable to geocode"}'
|
||||
|
||||
locales = options.get('locales', napi.Locales())
|
||||
|
||||
out = JsonWriter()
|
||||
|
||||
out.start_object()\
|
||||
@ -161,11 +155,6 @@ def format_base_geojson(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
.key('features').start_array()
|
||||
|
||||
for result in results:
|
||||
if result.address_rows:
|
||||
label_parts = result.address_rows.localize(locales)
|
||||
else:
|
||||
label_parts = []
|
||||
|
||||
out.start_object()\
|
||||
.keyval('type', 'Feature')\
|
||||
.key('properties').start_object()
|
||||
@ -181,8 +170,8 @@ def format_base_geojson(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
.keyval('addresstype', cl.get_label_tag(result.category, result.extratags,
|
||||
result.rank_address,
|
||||
result.country_code))\
|
||||
.keyval('name', locales.display_name(result.names))\
|
||||
.keyval('display_name', ', '.join(label_parts))
|
||||
.keyval('name', result.locale_name or '')\
|
||||
.keyval('display_name', result.display_name or '')
|
||||
|
||||
if options.get('addressdetails', False):
|
||||
out.key('address').start_object()
|
||||
@ -219,8 +208,6 @@ def format_base_geocodejson(results: Union[napi.ReverseResults, napi.SearchResul
|
||||
if not results and simple:
|
||||
return '{"error":"Unable to geocode"}'
|
||||
|
||||
locales = options.get('locales', napi.Locales())
|
||||
|
||||
out = JsonWriter()
|
||||
|
||||
out.start_object()\
|
||||
@ -234,11 +221,6 @@ def format_base_geocodejson(results: Union[napi.ReverseResults, napi.SearchResul
|
||||
.key('features').start_array()
|
||||
|
||||
for result in results:
|
||||
if result.address_rows:
|
||||
label_parts = result.address_rows.localize(locales)
|
||||
else:
|
||||
label_parts = []
|
||||
|
||||
out.start_object()\
|
||||
.keyval('type', 'Feature')\
|
||||
.key('properties').start_object()\
|
||||
@ -252,8 +234,8 @@ def format_base_geocodejson(results: Union[napi.ReverseResults, napi.SearchResul
|
||||
.keyval('osm_value', result.category[1])\
|
||||
.keyval('type', GEOCODEJSON_RANKS[max(3, min(28, result.rank_address))])\
|
||||
.keyval_not_none('accuracy', getattr(result, 'distance', None), transform=int)\
|
||||
.keyval('label', ', '.join(label_parts))\
|
||||
.keyval_not_none('name', result.names, transform=locales.display_name)\
|
||||
.keyval('label', result.display_name or '')\
|
||||
.keyval_not_none('name', result.locale_name or None)\
|
||||
|
||||
if options.get('addressdetails', False):
|
||||
_write_geocodejson_address(out, result.address_rows, result.place_id,
|
||||
|
@ -37,13 +37,7 @@ def _write_xml_address(root: ET.Element, address: napi.AddressLines,
|
||||
|
||||
|
||||
def _create_base_entry(result: Union[napi.ReverseResult, napi.SearchResult],
|
||||
root: ET.Element, simple: bool,
|
||||
locales: napi.Locales) -> ET.Element:
|
||||
if result.address_rows:
|
||||
label_parts = result.address_rows.localize(locales)
|
||||
else:
|
||||
label_parts = []
|
||||
|
||||
root: ET.Element, simple: bool) -> ET.Element:
|
||||
place = ET.SubElement(root, 'result' if simple else 'place')
|
||||
if result.place_id is not None:
|
||||
place.set('place_id', str(result.place_id))
|
||||
@ -54,9 +48,9 @@ def _create_base_entry(result: Union[napi.ReverseResult, napi.SearchResult],
|
||||
place.set('osm_id', str(result.osm_object[1]))
|
||||
if result.names and 'ref' in result.names:
|
||||
place.set('ref', result.names['ref'])
|
||||
elif label_parts:
|
||||
elif result.locale_name:
|
||||
# bug reproduced from PHP
|
||||
place.set('ref', label_parts[0])
|
||||
place.set('ref', result.locale_name)
|
||||
place.set('lat', f"{result.centroid.lat:.7f}")
|
||||
place.set('lon', f"{result.centroid.lon:.7f}")
|
||||
|
||||
@ -78,9 +72,9 @@ def _create_base_entry(result: Union[napi.ReverseResult, napi.SearchResult],
|
||||
place.set('geojson', result.geometry['geojson'])
|
||||
|
||||
if simple:
|
||||
place.text = ', '.join(label_parts)
|
||||
place.text = result.display_name or ''
|
||||
else:
|
||||
place.set('display_name', ', '.join(label_parts))
|
||||
place.set('display_name', result.display_name or '')
|
||||
place.set('class', result.category[0])
|
||||
place.set('type', result.category[1])
|
||||
place.set('importance', str(result.calculated_importance()))
|
||||
@ -95,8 +89,6 @@ def format_base_xml(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
""" Format the result into an XML response. With 'simple' exactly one
|
||||
result will be output, otherwise a list.
|
||||
"""
|
||||
locales = options.get('locales', napi.Locales())
|
||||
|
||||
root = ET.Element(xml_root_tag)
|
||||
root.set('timestamp', dt.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S +00:00'))
|
||||
root.set('attribution', cl.OSM_ATTRIBUTION)
|
||||
@ -107,7 +99,7 @@ def format_base_xml(results: Union[napi.ReverseResults, napi.SearchResults],
|
||||
ET.SubElement(root, 'error').text = 'Unable to geocode'
|
||||
|
||||
for result in results:
|
||||
place = _create_base_entry(result, root, simple, locales)
|
||||
place = _create_base_entry(result, root, simple)
|
||||
|
||||
if not simple and options.get('icon_base_url', None):
|
||||
icon = cl.ICONS.get(result.category)
|
||||
|
@ -305,6 +305,8 @@ async def details_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) ->
|
||||
if result is None:
|
||||
params.raise_error('No place with that OSM ID found.', status=404)
|
||||
|
||||
result.localize(locales)
|
||||
|
||||
output = formatting.format_result(result, fmt,
|
||||
{'locales': locales,
|
||||
'group_hierarchy': params.get_bool('group_hierarchy', False),
|
||||
@ -330,11 +332,13 @@ async def reverse_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) ->
|
||||
if debug:
|
||||
return params.build_response(loglib.get_and_disable())
|
||||
|
||||
fmt_options = {'locales': locales,
|
||||
'extratags': params.get_bool('extratags', False),
|
||||
fmt_options = {'extratags': params.get_bool('extratags', False),
|
||||
'namedetails': params.get_bool('namedetails', False),
|
||||
'addressdetails': params.get_bool('addressdetails', True)}
|
||||
|
||||
if result:
|
||||
result.localize(locales)
|
||||
|
||||
output = formatting.format_result(napi.ReverseResults([result] if result else []),
|
||||
fmt, fmt_options)
|
||||
|
||||
@ -363,11 +367,13 @@ async def lookup_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> A
|
||||
if debug:
|
||||
return params.build_response(loglib.get_and_disable())
|
||||
|
||||
fmt_options = {'locales': locales,
|
||||
'extratags': params.get_bool('extratags', False),
|
||||
fmt_options = {'extratags': params.get_bool('extratags', False),
|
||||
'namedetails': params.get_bool('namedetails', False),
|
||||
'addressdetails': params.get_bool('addressdetails', True)}
|
||||
|
||||
for result in results:
|
||||
result.localize(locales)
|
||||
|
||||
output = formatting.format_result(results, fmt, fmt_options)
|
||||
|
||||
return params.build_response(output)
|
||||
|
@ -179,11 +179,11 @@ class APIReverse:
|
||||
return 0
|
||||
|
||||
if result:
|
||||
result.localize(args.get_locales(api.config.DEFAULT_LANGUAGE))
|
||||
output = api_output.format_result(
|
||||
napi.ReverseResults([result]),
|
||||
args.format,
|
||||
{'locales': args.get_locales(api.config.DEFAULT_LANGUAGE),
|
||||
'extratags': args.extratags,
|
||||
{'extratags': args.extratags,
|
||||
'namedetails': args.namedetails,
|
||||
'addressdetails': args.addressdetails})
|
||||
if args.format != 'xml':
|
||||
@ -236,11 +236,13 @@ class APILookup:
|
||||
geometry_output=args.get_geometry_output(),
|
||||
geometry_simplification=args.polygon_threshold or 0.0)
|
||||
|
||||
for result in results:
|
||||
result.localize(args.get_locales(api.config.DEFAULT_LANGUAGE))
|
||||
|
||||
output = api_output.format_result(
|
||||
results,
|
||||
args.format,
|
||||
{'locales': args.get_locales(api.config.DEFAULT_LANGUAGE),
|
||||
'extratags': args.extratags,
|
||||
{'extratags': args.extratags,
|
||||
'namedetails': args.namedetails,
|
||||
'addressdetails': args.addressdetails})
|
||||
if args.format != 'xml':
|
||||
@ -320,10 +322,13 @@ class APIDetails:
|
||||
|
||||
|
||||
if result:
|
||||
locales = args.get_locales(api.config.DEFAULT_LANGUAGE)
|
||||
result.localize(locales)
|
||||
|
||||
output = api_output.format_result(
|
||||
result,
|
||||
'json',
|
||||
{'locales': args.get_locales(api.config.DEFAULT_LANGUAGE),
|
||||
{'locales': locales,
|
||||
'group_hierarchy': args.group_hierarchy})
|
||||
# reformat the result, so it is pretty-printed
|
||||
json.dump(json.loads(output), sys.stdout, indent=4, ensure_ascii=False)
|
||||
|
@ -75,11 +75,14 @@ def test_search_details_minimal():
|
||||
{'category': 'place',
|
||||
'type': 'thing',
|
||||
'admin_level': 15,
|
||||
'names': {},
|
||||
'localname': '',
|
||||
'calculated_importance': pytest.approx(0.0000001),
|
||||
'rank_address': 30,
|
||||
'rank_search': 30,
|
||||
'isarea': False,
|
||||
'addresstags': {},
|
||||
'extratags': {},
|
||||
'centroid': {'type': 'Point', 'coordinates': [1.0, 2.0]},
|
||||
'geometry': {'type': 'Point', 'coordinates': [1.0, 2.0]},
|
||||
}
|
||||
@ -108,6 +111,7 @@ def test_search_details_full():
|
||||
country_code='ll',
|
||||
indexed_date = import_date
|
||||
)
|
||||
search.localize(napi.Locales())
|
||||
|
||||
result = api_impl.format_result(search, 'json', {})
|
||||
|
||||
|
@ -101,6 +101,7 @@ def test_format_reverse_with_address(fmt):
|
||||
rank_address=10,
|
||||
distance=0.0)
|
||||
]))
|
||||
reverse.localize(napi.Locales())
|
||||
|
||||
raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
|
||||
{'addressdetails': True})
|
||||
@ -164,6 +165,8 @@ def test_format_reverse_geocodejson_special_parts():
|
||||
distance=0.0)
|
||||
]))
|
||||
|
||||
reverse.localize(napi.Locales())
|
||||
|
||||
raw = api_impl.format_result(napi.ReverseResults([reverse]), 'geocodejson',
|
||||
{'addressdetails': True})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user