2 Command-line interface to the Nominatim functions for import, update,
3 database administration and querying.
11 from pathlib import Path
13 from .config import Configuration
14 from .tools.exec_utils import run_legacy_script, run_api_script
15 from .db.connection import connect
16 from .db import status
18 LOG = logging.getLogger()
20 def _num_system_cpus():
22 cpus = len(os.sched_getaffinity(0))
23 except NotImplementedError:
26 return cpus or os.cpu_count()
29 class CommandlineParser:
30 """ Wraps some of the common functions for parsing the command line
31 and setting up subcommands.
33 def __init__(self, prog, description):
34 self.parser = argparse.ArgumentParser(
36 description=description,
37 formatter_class=argparse.RawDescriptionHelpFormatter)
39 self.subs = self.parser.add_subparsers(title='available commands',
42 # Arguments added to every sub-command
43 self.default_args = argparse.ArgumentParser(add_help=False)
44 group = self.default_args.add_argument_group('Default arguments')
45 group.add_argument('-h', '--help', action='help',
46 help='Show this help message and exit')
47 group.add_argument('-q', '--quiet', action='store_const', const=0,
48 dest='verbose', default=1,
49 help='Print only error messages')
50 group.add_argument('-v', '--verbose', action='count', default=1,
51 help='Increase verboseness of output')
52 group.add_argument('--project-dir', metavar='DIR', default='.',
53 help='Base directory of the Nominatim installation (default:.)')
54 group.add_argument('-j', '--threads', metavar='NUM', type=int,
55 help='Number of parallel threads to use')
58 def add_subcommand(self, name, cmd):
59 """ Add a subcommand to the parser. The subcommand must be a class
60 with a function add_args() that adds the parameters for the
61 subcommand and a run() function that executes the command.
63 parser = self.subs.add_parser(name, parents=[self.default_args],
64 help=cmd.__doc__.split('\n', 1)[0],
65 description=cmd.__doc__,
66 formatter_class=argparse.RawDescriptionHelpFormatter,
68 parser.set_defaults(command=cmd)
71 def run(self, **kwargs):
72 """ Parse the command line arguments of the program and execute the
73 appropriate subcommand.
75 args = self.parser.parse_args(args=kwargs.get('cli_args'))
77 if args.subcommand is None:
78 self.parser.print_help()
81 for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'data_dir', 'phpcgi_path'):
82 setattr(args, arg, Path(kwargs[arg]))
83 args.project_dir = Path(args.project_dir)
85 logging.basicConfig(stream=sys.stderr,
86 format='%(asctime)s: %(message)s',
87 datefmt='%Y-%m-%d %H:%M:%S',
88 level=max(4 - args.verbose, 1) * 10)
90 args.config = Configuration(args.project_dir, args.data_dir / 'settings')
92 return args.command.run(args)
95 def _osm2pgsql_options_from_args(args, default_cache, default_threads):
96 """ Set up the stanadrd osm2pgsql from the command line arguments.
98 return dict(osm2pgsql=args.osm2pgsql_path,
99 osm2pgsql_cache=args.osm2pgsql_cache or default_cache,
100 osm2pgsql_style=args.config.get_import_style_file(),
101 threads=args.threads or default_threads,
102 dsn=args.config.get_libpq_dsn(),
103 flatnode_file=args.config.FLATNODE_FILE)
105 ##### Subcommand classes
107 # Each class needs to implement two functions: add_args() adds the CLI parameters
108 # for the subfunction, run() executes the subcommand.
110 # The class documentation doubles as the help text for the command. The
111 # first line is also used in the summary when calling the program without
114 # No need to document the functions each time.
115 # pylint: disable=C0111
120 Create a new Nominatim database from an OSM file.
124 def add_args(parser):
125 group_name = parser.add_argument_group('Required arguments')
126 group = group_name.add_mutually_exclusive_group(required=True)
127 group.add_argument('--osm-file',
128 help='OSM file to be imported.')
129 group.add_argument('--continue', dest='continue_at',
130 choices=['load-data', 'indexing', 'db-postprocess'],
131 help='Continue an import that was interrupted')
132 group = parser.add_argument_group('Optional arguments')
133 group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
134 help='Size of cache to be used by osm2pgsql (in MB)')
135 group.add_argument('--reverse-only', action='store_true',
136 help='Do not create tables and indexes for searching')
137 group.add_argument('--enable-debug-statements', action='store_true',
138 help='Include debug warning statements in SQL code')
139 group.add_argument('--no-partitions', action='store_true',
140 help="""Do not partition search indices
141 (speeds up import of single country extracts)""")
142 group.add_argument('--no-updates', action='store_true',
143 help="""Do not keep tables that are only needed for
144 updating the database later""")
145 group = parser.add_argument_group('Expert options')
146 group.add_argument('--ignore-errors', action='store_true',
147 help='Continue import even when errors in SQL are present')
148 group.add_argument('--index-noanalyse', action='store_true',
149 help='Do not perform analyse operations during index')
154 params = ['setup.php']
156 params.extend(('--all', '--osm-file', args.osm_file))
158 if args.continue_at == 'load-data':
159 params.append('--load-data')
160 if args.continue_at in ('load-data', 'indexing'):
161 params.append('--index')
162 params.extend(('--create-search-indices', '--create-country-names',
164 if args.osm2pgsql_cache:
165 params.extend(('--osm2pgsql-cache', args.osm2pgsql_cache))
166 if args.reverse_only:
167 params.append('--reverse-only')
168 if args.enable_debug_statements:
169 params.append('--enable-debug-statements')
170 if args.no_partitions:
171 params.append('--no-partitions')
173 params.append('--drop')
174 if args.ignore_errors:
175 params.append('--ignore-errors')
176 if args.index_noanalyse:
177 params.append('--index-noanalyse')
179 return run_legacy_script(*params, nominatim_env=args)
184 Make database read-only.
186 About half of data in the Nominatim database is kept only to be able to
187 keep the data up-to-date with new changes made in OpenStreetMap. This
188 command drops all this data and only keeps the part needed for geocoding
191 This command has the same effect as the `--no-updates` option for imports.
195 def add_args(parser):
200 return run_legacy_script('setup.php', '--drop', nominatim_env=args)
203 class SetupSpecialPhrases:
205 Maintain special phrases.
209 def add_args(parser):
210 group = parser.add_argument_group('Input arguments')
211 group.add_argument('--from-wiki', action='store_true',
212 help='Pull special phrases from the OSM wiki.')
213 group = parser.add_argument_group('Output arguments')
214 group.add_argument('-o', '--output', default='-',
215 help="""File to write the preprocessed phrases to.
216 If omitted, it will be written to stdout.""")
220 if args.output != '-':
221 raise NotImplementedError('Only output to stdout is currently implemented.')
222 return run_legacy_script('specialphrases.php', '--wiki-import', nominatim_env=args)
225 class UpdateReplication:
227 Update the database using an online replication service.
231 def add_args(parser):
232 group = parser.add_argument_group('Arguments for initialisation')
233 group.add_argument('--init', action='store_true',
234 help='Initialise the update process')
235 group.add_argument('--no-update-functions', dest='update_functions',
236 action='store_false',
237 help="""Do not update the trigger function to
238 support differential updates.""")
239 group = parser.add_argument_group('Arguments for updates')
240 group.add_argument('--check-for-updates', action='store_true',
241 help='Check if new updates are available and exit')
242 group.add_argument('--once', action='store_true',
243 help="""Download and apply updates only once. When
244 not set, updates are continuously applied""")
245 group.add_argument('--no-index', action='store_false', dest='do_index',
246 help="""Do not index the new data. Only applicable
247 together with --once""")
248 group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
249 help='Size of cache to be used by osm2pgsql (in MB)')
252 def _init_replication(args):
253 from .tools import replication, refresh
255 LOG.warning("Initialising replication updates")
256 conn = connect(args.config.get_libpq_dsn())
257 replication.init_replication(conn, base_url=args.config.REPLICATION_URL)
258 if args.update_functions:
259 LOG.warning("Create functions")
260 refresh.create_functions(conn, args.config, args.data_dir,
267 def _check_for_updates(args):
268 from .tools import replication
270 conn = connect(args.config.get_libpq_dsn())
271 ret = replication.check_for_updates(conn, base_url=args.config.REPLICATION_URL)
278 from .tools import replication
279 from .indexer.indexer import Indexer
281 params = _osm2pgsql_options_from_args(args, 2000, 1)
282 params.update(base_url=args.config.REPLICATION_URL,
283 update_interval=args.config.get_int('REPLICATION_UPDATE_INTERVAL'),
284 import_file=args.project_dir / 'osmosischange.osc',
285 max_diff_size=args.config.get_int('REPLICATION_MAX_DIFF'),
286 indexed_only=not args.once)
288 # Sanity check to not overwhelm the Geofabrik servers.
289 if 'download.geofabrik.de'in params['base_url']\
290 and params['update_interval'] < 86400:
291 LOG.fatal("Update interval too low for download.geofabrik.de.\n"
292 "Please check install documentation "
293 "(https://nominatim.org/release-docs/latest/admin/Import-and-Update#"
294 "setting-up-the-update-process).")
295 raise RuntimeError("Invalid replication update interval setting.")
298 if not args.do_index:
299 LOG.fatal("Indexing cannot be disabled when running updates continuously.")
300 raise RuntimeError("Bad arguments.")
301 recheck_interval = args.config.get_int('REPLICATION_RECHECK_INTERVAL')
304 conn = connect(args.config.get_libpq_dsn())
305 start = dt.datetime.now(dt.timezone.utc)
306 state = replication.update(conn, params)
307 status.log_status(conn, start, 'import')
310 if state is not replication.UpdateState.NO_CHANGES and args.do_index:
311 start = dt.datetime.now(dt.timezone.utc)
312 indexer = Indexer(args.config.get_libpq_dsn(),
314 indexer.index_boundaries(0, 30)
315 indexer.index_by_rank(0, 30)
317 conn = connect(args.config.get_libpq_dsn())
318 status.set_indexed(conn, True)
319 status.log_status(conn, start, 'index')
325 if state is replication.UpdateState.NO_CHANGES:
326 LOG.warning("No new changes. Sleeping for %d sec.", recheck_interval)
327 time.sleep(recheck_interval)
334 import osmium # pylint: disable=W0611
335 except ModuleNotFoundError:
336 LOG.fatal("pyosmium not installed. Replication functions not available.\n"
337 "To install pyosmium via pip: pip3 install osmium")
341 return UpdateReplication._init_replication(args)
343 if args.check_for_updates:
344 return UpdateReplication._check_for_updates(args)
346 return UpdateReplication._update(args)
350 Add additional data from a file or an online source.
352 Data is only imported, not indexed. You need to call `nominatim-update index`
353 to complete the process.
357 def add_args(parser):
358 group_name = parser.add_argument_group('Source')
359 group = group_name.add_mutually_exclusive_group(required=True)
360 group.add_argument('--file', metavar='FILE',
361 help='Import data from an OSM file')
362 group.add_argument('--diff', metavar='FILE',
363 help='Import data from an OSM diff file')
364 group.add_argument('--node', metavar='ID', type=int,
365 help='Import a single node from the API')
366 group.add_argument('--way', metavar='ID', type=int,
367 help='Import a single way from the API')
368 group.add_argument('--relation', metavar='ID', type=int,
369 help='Import a single relation from the API')
370 group.add_argument('--tiger-data', metavar='DIR',
371 help='Add housenumbers from the US TIGER census database.')
372 group = parser.add_argument_group('Extra arguments')
373 group.add_argument('--use-main-api', action='store_true',
374 help='Use OSM API instead of Overpass to download objects')
379 os.environ['NOMINATIM_TIGER_DATA_PATH'] = args.tiger_data
380 return run_legacy_script('setup.php', '--import-tiger-data', nominatim_env=args)
382 params = ['update.php']
384 params.extend(('--import-file', args.file))
386 params.extend(('--import-diff', args.diff))
388 params.extend(('--import-node', args.node))
390 params.extend(('--import-way', args.way))
392 params.extend(('--import-relation', args.relation))
393 if args.use_main_api:
394 params.append('--use-main-api')
395 return run_legacy_script(*params, nominatim_env=args)
400 Reindex all new and modified data.
404 def add_args(parser):
405 group = parser.add_argument_group('Filter arguments')
406 group.add_argument('--boundaries-only', action='store_true',
407 help="""Index only administrative boundaries.""")
408 group.add_argument('--no-boundaries', action='store_true',
409 help="""Index everything except administrative boundaries.""")
410 group.add_argument('--minrank', '-r', type=int, metavar='RANK', default=0,
411 help='Minimum/starting rank')
412 group.add_argument('--maxrank', '-R', type=int, metavar='RANK', default=30,
413 help='Maximum/finishing rank')
417 from .indexer.indexer import Indexer
419 indexer = Indexer(args.config.get_libpq_dsn(),
420 args.threads or _num_system_cpus() or 1)
422 if not args.no_boundaries:
423 indexer.index_boundaries(args.minrank, args.maxrank)
424 if not args.boundaries_only:
425 indexer.index_by_rank(args.minrank, args.maxrank)
427 if not args.no_boundaries and not args.boundaries_only \
428 and args.minrank == 0 and args.maxrank == 30:
429 conn = connect(args.config.get_libpq_dsn())
430 status.set_indexed(conn, True)
438 Recompute auxiliary data used by the indexing process.
440 These functions must not be run in parallel with other update commands.
444 def add_args(parser):
445 group = parser.add_argument_group('Data arguments')
446 group.add_argument('--postcodes', action='store_true',
447 help='Update postcode centroid table')
448 group.add_argument('--word-counts', action='store_true',
449 help='Compute frequency of full-word search terms')
450 group.add_argument('--address-levels', action='store_true',
451 help='Reimport address level configuration')
452 group.add_argument('--functions', action='store_true',
453 help='Update the PL/pgSQL functions in the database')
454 group.add_argument('--wiki-data', action='store_true',
455 help='Update Wikipedia/data importance numbers.')
456 group.add_argument('--importance', action='store_true',
457 help='Recompute place importances (expensive!)')
458 group.add_argument('--website', action='store_true',
459 help='Refresh the directory that serves the scripts for the web API')
460 group = parser.add_argument_group('Arguments for function refresh')
461 group.add_argument('--no-diff-updates', action='store_false', dest='diffs',
462 help='Do not enable code for propagating updates')
463 group.add_argument('--enable-debug-statements', action='store_true',
464 help='Enable debug warning statements in functions')
468 from .tools import refresh
471 LOG.warning("Update postcodes centroid")
472 conn = connect(args.config.get_libpq_dsn())
473 refresh.update_postcodes(conn, args.data_dir)
477 LOG.warning('Recompute frequency of full-word search terms')
478 conn = connect(args.config.get_libpq_dsn())
479 refresh.recompute_word_counts(conn, args.data_dir)
482 if args.address_levels:
483 cfg = Path(args.config.ADDRESS_LEVEL_CONFIG)
484 LOG.warning('Updating address levels from %s', cfg)
485 conn = connect(args.config.get_libpq_dsn())
486 refresh.load_address_levels_from_file(conn, cfg)
490 LOG.warning('Create functions')
491 conn = connect(args.config.get_libpq_dsn())
492 refresh.create_functions(conn, args.config, args.data_dir,
493 args.diffs, args.enable_debug_statements)
497 run_legacy_script('setup.php', '--import-wikipedia-articles',
498 nominatim_env=args, throw_on_fail=True)
499 # Attention: importance MUST come after wiki data import.
501 run_legacy_script('update.php', '--recompute-importance',
502 nominatim_env=args, throw_on_fail=True)
504 run_legacy_script('setup.php', '--setup-website',
505 nominatim_env=args, throw_on_fail=True)
510 class AdminCheckDatabase:
512 Check that the database is complete and operational.
516 def add_args(parser):
521 return run_legacy_script('check_import_finished.php', nominatim_env=args)
526 Warm database caches for search and reverse queries.
530 def add_args(parser):
531 group = parser.add_argument_group('Target arguments')
532 group.add_argument('--search-only', action='store_const', dest='target',
534 help="Only pre-warm tables for search queries")
535 group.add_argument('--reverse-only', action='store_const', dest='target',
537 help="Only pre-warm tables for reverse queries")
541 params = ['warm.php']
542 if args.target == 'reverse':
543 params.append('--reverse-only')
544 if args.target == 'search':
545 params.append('--search-only')
546 return run_legacy_script(*params, nominatim_env=args)
551 Export addresses as CSV file from the database.
555 def add_args(parser):
556 group = parser.add_argument_group('Output arguments')
557 group.add_argument('--output-type', default='street',
558 choices=('continent', 'country', 'state', 'county',
559 'city', 'suburb', 'street', 'path'),
560 help='Type of places to output (default: street)')
561 group.add_argument('--output-format',
562 default='street;suburb;city;county;state;country',
563 help="""Semicolon-separated list of address types
564 (see --output-type). Multiple ranks can be
565 merged into one column by simply using a
566 comma-separated list.""")
567 group.add_argument('--output-all-postcodes', action='store_true',
568 help="""List all postcodes for address instead of
569 just the most likely one""")
570 group.add_argument('--language',
571 help="""Preferred language for output
572 (use local name, if omitted)""")
573 group = parser.add_argument_group('Filter arguments')
574 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
575 help='Export only objects within country')
576 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
577 help='Export only children of this OSM node')
578 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
579 help='Export only children of this OSM way')
580 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
581 help='Export only children of this OSM relation')
586 params = ['export.php',
587 '--output-type', args.output_type,
588 '--output-format', args.output_format]
589 if args.output_all_postcodes:
590 params.append('--output-all-postcodes')
592 params.extend(('--language', args.language))
593 if args.restrict_to_country:
594 params.extend(('--restrict-to-country', args.restrict_to_country))
595 if args.restrict_to_osm_node:
596 params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
597 if args.restrict_to_osm_way:
598 params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
599 if args.restrict_to_osm_relation:
600 params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
602 return run_legacy_script(*params, nominatim_env=args)
605 ('street', 'housenumber and street'),
606 ('city', 'city, town or village'),
607 ('county', 'county'),
609 ('country', 'country'),
610 ('postalcode', 'postcode')
614 ('addressdetails', 'Include a breakdown of the address into elements.'),
615 ('extratags', """Include additional information if available
616 (e.g. wikipedia link, opening hours)."""),
617 ('namedetails', 'Include a list of alternative names.')
621 ('addressdetails', 'Include a breakdown of the address into elements.'),
622 ('keywords', 'Include a list of name keywords and address keywords.'),
623 ('linkedplaces', 'Include a details of places that are linked with this one.'),
624 ('hierarchy', 'Include details of places lower in the address hierarchy.'),
625 ('group_hierarchy', 'Group the places by type.'),
626 ('polygon_geojson', 'Include geometry of result.')
629 def _add_api_output_arguments(parser):
630 group = parser.add_argument_group('Output arguments')
631 group.add_argument('--format', default='jsonv2',
632 choices=['xml', 'json', 'jsonv2', 'geojson', 'geocodejson'],
633 help='Format of result')
634 for name, desc in EXTRADATA_PARAMS:
635 group.add_argument('--' + name, action='store_true', help=desc)
637 group.add_argument('--lang', '--accept-language', metavar='LANGS',
638 help='Preferred language order for presenting search results')
639 group.add_argument('--polygon-output',
640 choices=['geojson', 'kml', 'svg', 'text'],
641 help='Output geometry of results as a GeoJSON, KML, SVG or WKT.')
642 group.add_argument('--polygon-threshold', type=float, metavar='TOLERANCE',
643 help="""Simplify output geometry.
644 Parameter is difference tolerance in degrees.""")
649 Execute API search query.
653 def add_args(parser):
654 group = parser.add_argument_group('Query arguments')
655 group.add_argument('--query',
656 help='Free-form query string')
657 for name, desc in STRUCTURED_QUERY:
658 group.add_argument('--' + name, help='Structured query: ' + desc)
660 _add_api_output_arguments(parser)
662 group = parser.add_argument_group('Result limitation')
663 group.add_argument('--countrycodes', metavar='CC,..',
664 help='Limit search results to one or more countries.')
665 group.add_argument('--exclude_place_ids', metavar='ID,..',
666 help='List of search object to be excluded')
667 group.add_argument('--limit', type=int,
668 help='Limit the number of returned results')
669 group.add_argument('--viewbox', metavar='X1,Y1,X2,Y2',
670 help='Preferred area to find search results')
671 group.add_argument('--bounded', action='store_true',
672 help='Strictly restrict results to viewbox area')
674 group = parser.add_argument_group('Other arguments')
675 group.add_argument('--no-dedupe', action='store_false', dest='dedupe',
676 help='Do not remove duplicates from the result list')
682 params = dict(q=args.query)
684 params = {k : getattr(args, k) for k, _ in STRUCTURED_QUERY if getattr(args, k)}
686 for param, _ in EXTRADATA_PARAMS:
687 if getattr(args, param):
689 for param in ('format', 'countrycodes', 'exclude_place_ids', 'limit', 'viewbox'):
690 if getattr(args, param):
691 params[param] = getattr(args, param)
693 params['accept-language'] = args.lang
694 if args.polygon_output:
695 params['polygon_' + args.polygon_output] = '1'
696 if args.polygon_threshold:
697 params['polygon_threshold'] = args.polygon_threshold
699 params['bounded'] = '1'
701 params['dedupe'] = '0'
703 return run_api_script('search', args.project_dir,
704 phpcgi_bin=args.phpcgi_path, params=params)
708 Execute API reverse query.
712 def add_args(parser):
713 group = parser.add_argument_group('Query arguments')
714 group.add_argument('--lat', type=float, required=True,
715 help='Latitude of coordinate to look up (in WGS84)')
716 group.add_argument('--lon', type=float, required=True,
717 help='Longitude of coordinate to look up (in WGS84)')
718 group.add_argument('--zoom', type=int,
719 help='Level of detail required for the address')
721 _add_api_output_arguments(parser)
726 params = dict(lat=args.lat, lon=args.lon)
727 if args.zoom is not None:
728 params['zoom'] = args.zoom
730 for param, _ in EXTRADATA_PARAMS:
731 if getattr(args, param):
734 params['format'] = args.format
736 params['accept-language'] = args.lang
737 if args.polygon_output:
738 params['polygon_' + args.polygon_output] = '1'
739 if args.polygon_threshold:
740 params['polygon_threshold'] = args.polygon_threshold
742 return run_api_script('reverse', args.project_dir,
743 phpcgi_bin=args.phpcgi_path, params=params)
748 Execute API reverse query.
752 def add_args(parser):
753 group = parser.add_argument_group('Query arguments')
754 group.add_argument('--id', metavar='OSMID',
755 action='append', required=True, dest='ids',
756 help='OSM id to lookup in format <NRW><id> (may be repeated)')
758 _add_api_output_arguments(parser)
763 params = dict(osm_ids=','.join(args.ids))
765 for param, _ in EXTRADATA_PARAMS:
766 if getattr(args, param):
769 params['format'] = args.format
771 params['accept-language'] = args.lang
772 if args.polygon_output:
773 params['polygon_' + args.polygon_output] = '1'
774 if args.polygon_threshold:
775 params['polygon_threshold'] = args.polygon_threshold
777 return run_api_script('lookup', args.project_dir,
778 phpcgi_bin=args.phpcgi_path, params=params)
783 Execute API lookup query.
787 def add_args(parser):
788 group = parser.add_argument_group('Query arguments')
789 objs = group.add_mutually_exclusive_group(required=True)
790 objs.add_argument('--node', '-n', type=int,
791 help="Look up the OSM node with the given ID.")
792 objs.add_argument('--way', '-w', type=int,
793 help="Look up the OSM way with the given ID.")
794 objs.add_argument('--relation', '-r', type=int,
795 help="Look up the OSM relation with the given ID.")
796 objs.add_argument('--place_id', '-p', type=int,
797 help='Database internal identifier of the OSM object to look up.')
798 group.add_argument('--class', dest='object_class',
799 help="""Class type to disambiguated multiple entries
800 of the same object.""")
802 group = parser.add_argument_group('Output arguments')
803 for name, desc in DETAILS_SWITCHES:
804 group.add_argument('--' + name, action='store_true', help=desc)
805 group.add_argument('--lang', '--accept-language', metavar='LANGS',
806 help='Preferred language order for presenting search results')
811 params = dict(osmtype='N', osmid=args.node)
813 params = dict(osmtype='W', osmid=args.node)
815 params = dict(osmtype='R', osmid=args.node)
817 params = dict(place_id=args.place_id)
818 if args.object_class:
819 params['class'] = args.object_class
820 for name, _ in DETAILS_SWITCHES:
821 params[name] = '1' if getattr(args, name) else '0'
823 return run_api_script('details', args.project_dir,
824 phpcgi_bin=args.phpcgi_path, params=params)
829 Execute API status query.
833 def add_args(parser):
834 group = parser.add_argument_group('API parameters')
835 group.add_argument('--format', default='text', choices=['text', 'json'],
836 help='Format of result')
840 return run_api_script('status', args.project_dir,
841 phpcgi_bin=args.phpcgi_path,
842 params=dict(format=args.format))
845 def nominatim(**kwargs):
847 Command-line tools for importing, updating, administrating and
848 querying the Nominatim database.
850 parser = CommandlineParser('nominatim', nominatim.__doc__)
852 parser.add_subcommand('import', SetupAll)
853 parser.add_subcommand('freeze', SetupFreeze)
854 parser.add_subcommand('replication', UpdateReplication)
856 parser.add_subcommand('check-database', AdminCheckDatabase)
857 parser.add_subcommand('warm', AdminWarm)
859 parser.add_subcommand('special-phrases', SetupSpecialPhrases)
861 parser.add_subcommand('add-data', UpdateAddData)
862 parser.add_subcommand('index', UpdateIndex)
863 parser.add_subcommand('refresh', UpdateRefresh)
865 parser.add_subcommand('export', QueryExport)
867 if kwargs.get('phpcgi_path'):
868 parser.add_subcommand('search', APISearch)
869 parser.add_subcommand('reverse', APIReverse)
870 parser.add_subcommand('lookup', APILookup)
871 parser.add_subcommand('details', APIDetails)
872 parser.add_subcommand('status', APIStatus)
874 parser.parser.epilog = 'php-cgi not found. Query commands not available.'
876 return parser.run(**kwargs)