"""
Helper functions for output of results in json formats.
"""
-from typing import Mapping, Any, Optional, Tuple
+from typing import Mapping, Any, Optional, Tuple, Union
import nominatim.api as napi
-from nominatim.api.v1.constants import OSM_ATTRIBUTION, OSM_TYPE_NAME, bbox_from_result
-from nominatim.api.v1.classtypes import ICONS, get_label_tag
+import nominatim.api.v1.classtypes as cl
from nominatim.utils.json_writer import JsonWriter
+#pylint: disable=too-many-branches
+
def _write_osm_id(out: JsonWriter, osm_object: Optional[Tuple[str, int]]) -> None:
if osm_object is not None:
- out.keyval_not_none('osm_type', OSM_TYPE_NAME.get(osm_object[0], None))\
+ out.keyval_not_none('osm_type', cl.OSM_TYPE_NAME.get(osm_object[0], None))\
.keyval('osm_id', osm_object[1])
country_code: Optional[str]) -> None:
parts = {}
for line in (address or []):
- if line.isaddress and line.local_name:
- label = get_label_tag(line.category, line.extratags,
- line.rank_address, country_code)
- if label not in parts:
- parts[label] = line.local_name
+ if line.isaddress:
+ if line.local_name:
+ label = cl.get_label_tag(line.category, line.extratags,
+ line.rank_address, country_code)
+ if label not in parts:
+ parts[label] = line.local_name
+ if line.names and 'ISO3166-2' in line.names and line.admin_level:
+ parts[f"ISO3166-2-lvl{line.admin_level}"] = line.names['ISO3166-2']
for k, v in parts.items():
out.keyval(k, v)
out.keyval('housenumber', line.local_name)
elif (obj_place_id is None or obj_place_id != line.place_id) \
and line.rank_address >= 4 and line.rank_address < 28:
- extra[GEOCODEJSON_RANKS[line.rank_address]] = line.local_name
+ rank_name = GEOCODEJSON_RANKS[line.rank_address]
+ if rank_name not in extra:
+ extra[rank_name] = line.local_name
+
for k, v in extra.items():
out.keyval(k, v)
out.keyval('country_code', country_code)
-def format_base_json(results: napi.ReverseResults, #pylint: disable=too-many-branches
+def format_base_json(results: Union[napi.ReverseResults, napi.SearchResults],
options: Mapping[str, Any], simple: bool,
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:
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', OSM_ATTRIBUTION)\
+ .keyval('licence', cl.OSM_ATTRIBUTION)\
_write_osm_id(out, result.osm_object)
- out.keyval('lat', result.centroid.lat)\
- .keyval('lon', result.centroid.lon)\
+ out.keyval('lat', f"{result.centroid.lat}")\
+ .keyval('lon', f"{result.centroid.lon}")\
.keyval(class_label, result.category[0])\
.keyval('type', result.category[1])\
.keyval('place_rank', result.rank_search)\
.keyval('importance', result.calculated_importance())\
- .keyval('addresstype', 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('addresstype', cl.get_label_tag(result.category, result.extratags,
+ result.rank_address,
+ result.country_code))\
+ .keyval('name', result.locale_name or '')\
+ .keyval('display_name', result.display_name or '')
if options.get('icon_base_url', None):
- icon = ICONS.get(result.category)
+ icon = cl.ICONS.get(result.category)
if icon:
out.keyval('icon', f"{options['icon_base_url']}/{icon}.p.20.png")
if options.get('namedetails', False):
out.keyval('namedetails', result.names)
- bbox = bbox_from_result(result)
+ bbox = cl.bbox_from_result(result)
out.key('boundingbox').start_array()\
- .value(bbox.minlat).next()\
- .value(bbox.maxlat).next()\
- .value(bbox.minlon).next()\
- .value(bbox.maxlon).next()\
+ .value(f"{bbox.minlat:0.7f}").next()\
+ .value(f"{bbox.maxlat:0.7f}").next()\
+ .value(f"{bbox.minlon:0.7f}").next()\
+ .value(f"{bbox.maxlon:0.7f}").next()\
.end_array().next()
if result.geometry:
return out()
-def format_base_geojson(results: napi.ReverseResults,
+def format_base_geojson(results: Union[napi.ReverseResults, napi.SearchResults],
options: Mapping[str, Any],
simple: bool) -> str:
""" Return the result list as a geojson string.
if not results and simple:
return '{"error":"Unable to geocode"}'
- locales = options.get('locales', napi.Locales())
-
out = JsonWriter()
out.start_object()\
.keyval('type', 'FeatureCollection')\
- .keyval('licence', OSM_ATTRIBUTION)\
+ .keyval('licence', cl.OSM_ATTRIBUTION)\
.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()
.keyval('category', result.category[0])\
.keyval('type', result.category[1])\
.keyval('importance', result.calculated_importance())\
- .keyval('addresstype', 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('addresstype', cl.get_label_tag(result.category, result.extratags,
+ result.rank_address,
+ result.country_code))\
+ .keyval('name', result.locale_name or '')\
+ .keyval('display_name', result.display_name or '')
if options.get('addressdetails', False):
out.key('address').start_object()
out.end_object().next() # properties
- bbox = bbox_from_result(result)
- out.keyval('bbox', bbox.coords)
+ out.key('bbox').start_array()
+ for coord in cl.bbox_from_result(result).coords:
+ out.float(coord, 7).next()
+ out.end_array().next()
out.key('geometry').raw(result.geometry.get('geojson')
or result.centroid.to_geojson()).next()
return out()
-def format_base_geocodejson(results: napi.ReverseResults,
+def format_base_geocodejson(results: Union[napi.ReverseResults, napi.SearchResults],
options: Mapping[str, Any], simple: bool) -> str:
""" Return the result list as a geocodejson string.
"""
if not results and simple:
return '{"error":"Unable to geocode"}'
- locales = options.get('locales', napi.Locales())
-
out = JsonWriter()
out.start_object()\
.keyval('type', 'FeatureCollection')\
.key('geocoding').start_object()\
.keyval('version', '0.1.0')\
- .keyval('attribution', OSM_ATTRIBUTION)\
+ .keyval('attribution', cl.OSM_ATTRIBUTION)\
.keyval('licence', 'ODbL')\
.keyval_not_none('query', options.get('query'))\
.end_object().next()\
.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()\
out.keyval('osm_key', result.category[0])\
.keyval('osm_value', result.category[1])\
.keyval('type', GEOCODEJSON_RANKS[max(3, min(28, result.rank_address))])\
- .keyval_not_none('accuracy', result.distance)\
- .keyval('label', ', '.join(label_parts))\
- .keyval_not_none('name', locales.display_name(result.names))\
+ .keyval_not_none('accuracy', getattr(result, 'distance', None), transform=int)\
+ .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,
out.key('admin').start_object()
if result.address_rows:
for line in result.address_rows:
- if line.isaddress and (line.admin_level or 15) < 15 and line.local_name:
+ if line.isaddress and (line.admin_level or 15) < 15 and line.local_name \
+ and line.category[0] == 'boundary' and line.category[1] == 'administrative':
out.keyval(f"level{line.admin_level}", line.local_name)
out.end_object().next()