From 15eb7f0bb1437e5a8463e32e8c9915595185798a Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Sat, 28 Sep 2024 10:10:58 +0200 Subject: [PATCH 1/3] add new format 'raw' for CLI commands This dumps the original results with all details available. --- src/nominatim_db/clicmd/api.py | 38 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/nominatim_db/clicmd/api.py b/src/nominatim_db/clicmd/api.py index dcbbb24bb..000a60329 100644 --- a/src/nominatim_db/clicmd/api.py +++ b/src/nominatim_db/clicmd/api.py @@ -12,6 +12,7 @@ import logging import json import sys +import pprint from functools import reduce import nominatim_api as napi @@ -113,24 +114,29 @@ def _list_formats(formatter: napi.FormatDispatcher, rtype: Type[Any]) -> int: for fmt in formatter.list_formats(rtype): print(fmt) print('debug') + print('raw') return 0 def _print_output(formatter: napi.FormatDispatcher, result: Any, fmt: str, options: Mapping[str, Any]) -> None: - output = formatter.format_result(result, fmt, options) - if formatter.get_content_type(fmt) == CONTENT_JSON: - # reformat the result, so it is pretty-printed - try: - json.dump(json.loads(output), sys.stdout, indent=4, ensure_ascii=False) - except json.decoder.JSONDecodeError as err: - # Catch the error here, so that data can be debugged, - # when people are developping custom result formatters. - LOG.fatal("Parsing json failed: %s\nUnformatted output:\n%s", err, output) + + if fmt == 'raw': + pprint.pprint(result) else: - sys.stdout.write(output) - sys.stdout.write('\n') + output = formatter.format_result(result, fmt, options) + if formatter.get_content_type(fmt) == CONTENT_JSON: + # reformat the result, so it is pretty-printed + try: + json.dump(json.loads(output), sys.stdout, indent=4, ensure_ascii=False) + except json.decoder.JSONDecodeError as err: + # Catch the error here, so that data can be debugged, + # when people are developping custom result formatters. + LOG.fatal("Parsing json failed: %s\nUnformatted output:\n%s", err, output) + else: + sys.stdout.write(output) + sys.stdout.write('\n') class APISearch: @@ -174,7 +180,7 @@ def run(self, args: NominatimArgs) -> int: if args.list_formats: return _list_formats(formatter, napi.SearchResults) - if args.format == 'debug': + if args.format in ('debug', 'raw'): loglib.set_log_output('text') elif not formatter.supports_format(napi.SearchResults, args.format): raise UsageError(f"Unsupported format '{args.format}'. " @@ -254,7 +260,7 @@ def run(self, args: NominatimArgs) -> int: if args.list_formats: return _list_formats(formatter, napi.ReverseResults) - if args.format == 'debug': + if args.format in ('debug', 'raw'): loglib.set_log_output('text') elif not formatter.supports_format(napi.ReverseResults, args.format): raise UsageError(f"Unsupported format '{args.format}'. " @@ -320,7 +326,7 @@ def run(self, args: NominatimArgs) -> int: if args.list_formats: return _list_formats(formatter, napi.ReverseResults) - if args.format == 'debug': + if args.format in ('debug', 'raw'): loglib.set_log_output('text') elif not formatter.supports_format(napi.ReverseResults, args.format): raise UsageError(f"Unsupported format '{args.format}'. " @@ -402,7 +408,7 @@ def run(self, args: NominatimArgs) -> int: if args.list_formats: return _list_formats(formatter, napi.DetailedResult) - if args.format == 'debug': + if args.format in ('debug', 'raw'): loglib.set_log_output('text') elif not formatter.supports_format(napi.DetailedResult, args.format): raise UsageError(f"Unsupported format '{args.format}'. " @@ -473,7 +479,7 @@ def run(self, args: NominatimArgs) -> int: if args.list_formats: return _list_formats(formatter, napi.StatusResult) - if args.format == 'debug': + if args.format in ('debug', 'raw'): loglib.set_log_output('text') elif not formatter.supports_format(napi.StatusResult, args.format): raise UsageError(f"Unsupported format '{args.format}'. " From 83013f819b463b14332854dd80cc47629bea0d1a Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Sat, 28 Sep 2024 13:45:32 +0200 Subject: [PATCH 2/3] derive bbox size for postcode nodes from rank_search --- src/nominatim_api/v1/classtypes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/nominatim_api/v1/classtypes.py b/src/nominatim_api/v1/classtypes.py index 66708593b..9e78c83b5 100644 --- a/src/nominatim_api/v1/classtypes.py +++ b/src/nominatim_api/v1/classtypes.py @@ -48,7 +48,12 @@ def bbox_from_result(result: Union[ReverseResult, SearchResult]) -> Bbox: around the centroid according to dimensions derived from the search rank. """ + if result.category == ('place', 'postcode') and result.bbox is None: + return Bbox.from_point(result.centroid, + 0.05 - 0.012 * (result.rank_search - 21)) + if (result.osm_object and result.osm_object[0] == 'N') or result.bbox is None: + extent = NODE_EXTENT.get(result.category, 0.00005) return Bbox.from_point(result.centroid, extent) From 5160a1d57758ca95bb22d8372f6e84fb8f58e191 Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Sat, 28 Sep 2024 14:42:32 +0200 Subject: [PATCH 3/3] get bbox of postcode areas into results --- src/nominatim_api/search/db_searches.py | 2 ++ test/python/api/search/test_search_postcode.py | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/nominatim_api/search/db_searches.py b/src/nominatim_api/search/db_searches.py index 35c063fc9..d2202b2cc 100644 --- a/src/nominatim_api/search/db_searches.py +++ b/src/nominatim_api/search/db_searches.py @@ -616,6 +616,8 @@ async def lookup(self, conn: SearchConnection, for prow in await conn.execute(placex_sql, _details_to_bind_params(details)): result = nres.create_from_placex_row(prow, nres.SearchResult) + if result is not None: + result.bbox = Bbox.from_wkb(prow.bbox) break else: result = nres.create_from_postcode_row(row, nres.SearchResult) diff --git a/test/python/api/search/test_search_postcode.py b/test/python/api/search/test_search_postcode.py index 633e07bcf..369e15045 100644 --- a/test/python/api/search/test_search_postcode.py +++ b/test/python/api/search/test_search_postcode.py @@ -59,6 +59,19 @@ def test_postcode_with_country(apiobj, frontend): assert results[0].place_id == 101 +def test_postcode_area(apiobj, frontend): + apiobj.add_postcode(place_id=100, country_code='ch', postcode='12345') + apiobj.add_placex(place_id=200, country_code='ch', postcode='12345', + osm_type='R', osm_id=34, class_='boundary', type='postal_code', + geometry='POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))') + + results = run_search(apiobj, frontend, 0.3, ['12345'], [0.0]) + + assert len(results) == 1 + assert results[0].place_id == 200 + assert results[0].bbox.area == 1 + + class TestPostcodeSearchWithAddress: @pytest.fixture(autouse=True)