]> git.openstreetmap.org Git - nominatim.git/blobdiff - src/nominatim_db/clicmd/api.py
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / src / nominatim_db / clicmd / api.py
index dcbbb24bb4bb785c13a2389162a5c0fd1dfa96ab..a922dc89fedb3231738d1235323fc1ce1d8bbea2 100644 (file)
@@ -12,6 +12,7 @@ import argparse
 import logging
 import json
 import sys
 import logging
 import json
 import sys
+import pprint
 from functools import reduce
 
 import nominatim_api as napi
 from functools import reduce
 
 import nominatim_api as napi
@@ -21,11 +22,10 @@ import nominatim_api.logging as loglib
 from ..errors import UsageError
 from .args import NominatimArgs
 
 from ..errors import UsageError
 from .args import NominatimArgs
 
-# Do not repeat documentation of subcommand classes.
-# pylint: disable=C0111
 
 LOG = logging.getLogger()
 
 
 LOG = logging.getLogger()
 
+
 STRUCTURED_QUERY = (
     ('amenity', 'name and/or type of POI'),
     ('street', 'housenumber and street'),
 STRUCTURED_QUERY = (
     ('amenity', 'name and/or type of POI'),
     ('street', 'housenumber and street'),
@@ -36,6 +36,7 @@ STRUCTURED_QUERY = (
     ('postalcode', 'postcode')
 )
 
     ('postalcode', 'postcode')
 )
 
+
 EXTRADATA_PARAMS = (
     ('addressdetails', 'Include a breakdown of the address into elements'),
     ('extratags', ("Include additional information if available "
 EXTRADATA_PARAMS = (
     ('addressdetails', 'Include a breakdown of the address into elements'),
     ('extratags', ("Include additional information if available "
@@ -43,6 +44,7 @@ EXTRADATA_PARAMS = (
     ('namedetails', 'Include a list of alternative names')
 )
 
     ('namedetails', 'Include a list of alternative names')
 )
 
+
 def _add_list_format(parser: argparse.ArgumentParser) -> None:
     group = parser.add_argument_group('Other options')
     group.add_argument('--list-formats', action='store_true',
 def _add_list_format(parser: argparse.ArgumentParser) -> None:
     group = parser.add_argument_group('Other options')
     group.add_argument('--list-formats', action='store_true',
@@ -61,7 +63,7 @@ def _add_api_output_arguments(parser: argparse.ArgumentParser) -> None:
     group.add_argument('--polygon-output',
                        choices=['geojson', 'kml', 'svg', 'text'],
                        help='Output geometry of results as a GeoJSON, KML, SVG or WKT')
     group.add_argument('--polygon-output',
                        choices=['geojson', 'kml', 'svg', 'text'],
                        help='Output geometry of results as a GeoJSON, KML, SVG or WKT')
-    group.add_argument('--polygon-threshold', type=float, default = 0.0,
+    group.add_argument('--polygon-threshold', type=float, default=0.0,
                        metavar='TOLERANCE',
                        help=("Simplify output geometry."
                              "Parameter is difference tolerance in degrees."))
                        metavar='TOLERANCE',
                        help=("Simplify output geometry."
                              "Parameter is difference tolerance in degrees."))
@@ -113,24 +115,29 @@ def _list_formats(formatter: napi.FormatDispatcher, rtype: Type[Any]) -> int:
     for fmt in formatter.list_formats(rtype):
         print(fmt)
     print('debug')
     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:
 
     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:
     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:
 
 
 class APISearch:
@@ -167,14 +174,13 @@ class APISearch:
                            help='Do not remove duplicates from the result list')
         _add_list_format(parser)
 
                            help='Do not remove duplicates from the result list')
         _add_list_format(parser)
 
-
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         if args.list_formats:
             return _list_formats(formatter, napi.SearchResults)
 
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         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}'. "
             loglib.set_log_output('text')
         elif not formatter.supports_format(napi.SearchResults, args.format):
             raise UsageError(f"Unsupported format '{args.format}'. "
@@ -183,7 +189,7 @@ class APISearch:
         try:
             with napi.NominatimAPI(args.project_dir) as api:
                 params: Dict[str, Any] = {'max_results': args.limit + min(args.limit, 10),
         try:
             with napi.NominatimAPI(args.project_dir) as api:
                 params: Dict[str, Any] = {'max_results': args.limit + min(args.limit, 10),
-                                          'address_details': True, # needed for display name
+                                          'address_details': True,  # needed for display name
                                           'geometry_output': _get_geometry_output(args),
                                           'geometry_simplification': args.polygon_threshold,
                                           'countries': args.countrycodes,
                                           'geometry_output': _get_geometry_output(args),
                                           'geometry_simplification': args.polygon_threshold,
                                           'countries': args.countrycodes,
@@ -191,7 +197,7 @@ class APISearch:
                                           'viewbox': args.viewbox,
                                           'bounded_viewbox': args.bounded,
                                           'locales': _get_locales(args, api.config.DEFAULT_LANGUAGE)
                                           'viewbox': args.viewbox,
                                           'bounded_viewbox': args.bounded,
                                           'locales': _get_locales(args, api.config.DEFAULT_LANGUAGE)
-                                         }
+                                          }
 
                 if args.query:
                     results = api.search(args.query, **params)
 
                 if args.query:
                     results = api.search(args.query, **params)
@@ -247,14 +253,13 @@ class APIReverse:
         _add_api_output_arguments(parser)
         _add_list_format(parser)
 
         _add_api_output_arguments(parser)
         _add_list_format(parser)
 
-
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         if args.list_formats:
             return _list_formats(formatter, napi.ReverseResults)
 
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         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}'. "
             loglib.set_log_output('text')
         elif not formatter.supports_format(napi.ReverseResults, args.format):
             raise UsageError(f"Unsupported format '{args.format}'. "
@@ -270,7 +275,7 @@ class APIReverse:
                 result = api.reverse(napi.Point(args.lon, args.lat),
                                      max_rank=zoom_to_rank(args.zoom or 18),
                                      layers=layers,
                 result = api.reverse(napi.Point(args.lon, args.lat),
                                      max_rank=zoom_to_rank(args.zoom or 18),
                                      layers=layers,
-                                     address_details=True, # needed for display name
+                                     address_details=True,  # needed for display name
                                      geometry_output=_get_geometry_output(args),
                                      geometry_simplification=args.polygon_threshold,
                                      locales=_get_locales(args, api.config.DEFAULT_LANGUAGE))
                                      geometry_output=_get_geometry_output(args),
                                      geometry_simplification=args.polygon_threshold,
                                      locales=_get_locales(args, api.config.DEFAULT_LANGUAGE))
@@ -293,7 +298,6 @@ class APIReverse:
         return 42
 
 
         return 42
 
 
-
 class APILookup:
     """\
     Execute API lookup query.
 class APILookup:
     """\
     Execute API lookup query.
@@ -313,14 +317,13 @@ class APILookup:
         _add_api_output_arguments(parser)
         _add_list_format(parser)
 
         _add_api_output_arguments(parser)
         _add_list_format(parser)
 
-
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         if args.list_formats:
             return _list_formats(formatter, napi.ReverseResults)
 
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         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}'. "
             loglib.set_log_output('text')
         elif not formatter.supports_format(napi.ReverseResults, args.format):
             raise UsageError(f"Unsupported format '{args.format}'. "
@@ -334,7 +337,7 @@ class APILookup:
         try:
             with napi.NominatimAPI(args.project_dir) as api:
                 results = api.lookup(places,
         try:
             with napi.NominatimAPI(args.project_dir) as api:
                 results = api.lookup(places,
-                                     address_details=True, # needed for display name
+                                     address_details=True,  # needed for display name
                                      geometry_output=_get_geometry_output(args),
                                      geometry_simplification=args.polygon_threshold or 0.0,
                                      locales=_get_locales(args, api.config.DEFAULT_LANGUAGE))
                                      geometry_output=_get_geometry_output(args),
                                      geometry_simplification=args.polygon_threshold or 0.0,
                                      locales=_get_locales(args, api.config.DEFAULT_LANGUAGE))
@@ -395,14 +398,13 @@ class APIDetails:
                            help='Preferred language order for presenting search results')
         _add_list_format(parser)
 
                            help='Preferred language order for presenting search results')
         _add_list_format(parser)
 
-
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         if args.list_formats:
             return _list_formats(formatter, napi.DetailedResult)
 
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         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}'. "
             loglib.set_log_output('text')
         elif not formatter.supports_format(napi.DetailedResult, args.format):
             raise UsageError(f"Unsupported format '{args.format}'. "
@@ -415,7 +417,7 @@ class APIDetails:
             place = napi.OsmID('W', args.way, args.object_class)
         elif args.relation:
             place = napi.OsmID('R', args.relation, args.object_class)
             place = napi.OsmID('W', args.way, args.object_class)
         elif args.relation:
             place = napi.OsmID('R', args.relation, args.object_class)
-        elif  args.place_id is not None:
+        elif args.place_id is not None:
             place = napi.PlaceID(args.place_id)
         else:
             raise UsageError('One of the arguments --node/-n --way/-w '
             place = napi.PlaceID(args.place_id)
         else:
             raise UsageError('One of the arguments --node/-n --way/-w '
@@ -429,10 +431,10 @@ class APIDetails:
                                      linked_places=args.linkedplaces,
                                      parented_places=args.hierarchy,
                                      keywords=args.keywords,
                                      linked_places=args.linkedplaces,
                                      parented_places=args.hierarchy,
                                      keywords=args.keywords,
-                                     geometry_output=napi.GeometryFormat.GEOJSON
-                                                     if args.polygon_geojson
-                                                     else napi.GeometryFormat.NONE,
-                                    locales=locales)
+                                     geometry_output=(napi.GeometryFormat.GEOJSON
+                                                      if args.polygon_geojson
+                                                      else napi.GeometryFormat.NONE),
+                                     locales=locales)
         except napi.UsageError as ex:
             raise UsageError(ex) from ex
 
         except napi.UsageError as ex:
             raise UsageError(ex) from ex
 
@@ -466,14 +468,13 @@ class APIStatus:
                            help='Format of result (use --list-formats to see supported formats)')
         _add_list_format(parser)
 
                            help='Format of result (use --list-formats to see supported formats)')
         _add_list_format(parser)
 
-
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         if args.list_formats:
             return _list_formats(formatter, napi.StatusResult)
 
     def run(self, args: NominatimArgs) -> int:
         formatter = napi.load_format_dispatcher('v1', args.project_dir)
 
         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}'. "
             loglib.set_log_output('text')
         elif not formatter.supports_format(napi.StatusResult, args.format):
             raise UsageError(f"Unsupported format '{args.format}'. "