2 Command-line interface to the Nominatim functions for import, update,
3 database administration and querying.
8 from pathlib import Path
10 from .config import Configuration
11 from .admin.exec_utils import run_legacy_script
13 class CommandlineParser:
14 """ Wraps some of the common functions for parsing the command line
15 and setting up subcommands.
17 def __init__(self, prog, description):
18 self.parser = argparse.ArgumentParser(
20 description=description,
21 formatter_class=argparse.RawDescriptionHelpFormatter)
23 self.subs = self.parser.add_subparsers(title='available commands',
26 # Arguments added to every sub-command
27 self.default_args = argparse.ArgumentParser(add_help=False)
28 group = self.default_args.add_argument_group('Default arguments')
29 group.add_argument('-h', '--help', action='help',
30 help='Show this help message and exit')
31 group.add_argument('-q', '--quiet', action='store_const', const=0,
32 dest='verbose', default=1,
33 help='Print only error messages')
34 group.add_argument('-v', '--verbose', action='count', default=1,
35 help='Increase verboseness of output')
36 group.add_argument('--project-dir', metavar='DIR', default='.',
37 help='Base directory of the Nominatim installation (default:.)')
38 group.add_argument('-j', '--threads', metavar='NUM', type=int,
39 help='Number of parallel threads to use')
42 def add_subcommand(self, name, cmd):
43 """ Add a subcommand to the parser. The subcommand must be a class
44 with a function add_args() that adds the parameters for the
45 subcommand and a run() function that executes the command.
47 parser = self.subs.add_parser(name, parents=[self.default_args],
48 help=cmd.__doc__.split('\n', 1)[0],
49 description=cmd.__doc__,
50 formatter_class=argparse.RawDescriptionHelpFormatter,
52 parser.set_defaults(command=cmd)
55 def run(self, **kwargs):
56 """ Parse the command line arguments of the program and execute the
57 appropriate subcommand.
59 args = self.parser.parse_args()
61 if args.subcommand is None:
62 return self.parser.print_help()
64 for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'data_dir'):
65 setattr(args, arg, Path(kwargs[arg]))
66 args.project_dir = Path(args.project_dir)
68 logging.basicConfig(stream=sys.stderr,
69 format='%(asctime)s %(levelname)s: %(message)s',
70 datefmt='%Y-%m-%d %H:%M:%S',
71 level=max(4 - args.verbose, 1) * 10)
73 args.config = Configuration(args.project_dir, args.data_dir / 'settings')
75 args.command.run(args)
80 Create a new Nominatim database from an OSM file.
85 group_name = parser.add_argument_group('Required arguments')
86 group = group_name.add_mutually_exclusive_group(required=True)
87 group.add_argument('--osm-file',
88 help='OSM file to be imported.')
89 group.add_argument('--continue', nargs=1, dest='continue_at',
90 choices=['load-data', 'indexing', 'db-postprocess'],
91 help='Continue an import that was interrupted')
92 group = parser.add_argument_group('Optional arguments')
93 group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
94 help='Size of cache to be used by osm2pgsql (in MB)')
95 group.add_argument('--reverse-only', action='store_true',
96 help='Do not create tables and indexes for searching')
97 group.add_argument('--enable-debug-statements', action='store_true',
98 help='Include debug warning statements in SQL code')
99 group.add_argument('--no-partitions', action='store_true',
100 help="""Do not partition search indices
101 (speeds up import of single country extracts)""")
102 group.add_argument('--no-updates', action='store_true',
103 help="""Do not keep tables that are only needed for
104 updating the database later""")
105 group = parser.add_argument_group('Expert options')
106 group.add_argument('--ignore-errors', action='store_true',
107 help='Continue import even when errors in SQL are present')
108 group.add_argument('--index-noanalyse', action='store_true',
109 help='Do not perform analyse operations during index')
114 params = ['setup.php']
116 params.extend(('--all', '--osm-file', args.osm_file))
118 if args.continue_at == 'load-data':
119 params.append('--load-data')
120 if args.continue_at in ('load-data', 'indexing'):
121 params.append('--index')
122 params.extend(('--create-search-indices', '--create-country-names',
124 if args.osm2pgsql_cache:
125 params.extend(('--osm2pgsql-cache', args.osm2pgsql_cache))
126 if args.reverse_only:
127 params.append('--reverse-only')
128 if args.enable_debug_statements:
129 params.append('--enable-debug-statements')
130 if args.no_partitions:
131 params.append('--no-partitions')
133 params.append('--drop')
134 if args.ignore_errors:
135 params.append('--ignore-errors')
136 if args.index_noanalyse:
137 params.append('--index-noanalyse')
139 return run_legacy_script(*params, nominatim_env=args)
144 Make database read-only.
146 About half of data in the Nominatim database is kept only to be able to
147 keep the data up-to-date with new changes made in OpenStreetMap. This
148 command drops all this data and only keeps the part needed for geocoding
151 This command has the same effect as the `--no-updates` option for imports.
155 def add_args(parser):
160 return run_legacy_script('setup.php', '--drop', nominatim_env=args)
163 class SetupSpecialPhrases:
165 Maintain special phrases.
169 def add_args(parser):
170 group = parser.add_argument_group('Input arguments')
171 group.add_argument('--from-wiki', action='store_true',
172 help='Pull special phrases from the OSM wiki.')
173 group = parser.add_argument_group('Output arguments')
174 group.add_argument('-o', '--output', default='-',
175 type=argparse.FileType('w', encoding='UTF-8'),
176 help="""File to write the preprocessed phrases to.
177 If omitted, it will be written to stdout.""")
181 if args.output.name != '<stdout>':
182 raise NotImplementedError('Only output to stdout is currently implemented.')
183 return run_legacy_script('specialphrases.php', '--wiki-import' , nominatim_env=args)
186 class UpdateReplication:
188 Update the database using an online replication service.
192 def add_args(parser):
193 group = parser.add_argument_group('Arguments for initialisation')
194 group.add_argument('--init', action='store_true',
195 help='Initialise the update process')
196 group.add_argument('--no-update-functions', dest='update_functions',
197 action='store_false',
198 help="""Do not update the trigger function to
199 support differential updates.""")
200 group = parser.add_argument_group('Arguments for updates')
201 group.add_argument('--check-for-updates', action='store_true',
202 help='Check if new updates are available and exit')
203 group.add_argument('--once', action='store_true',
204 help="""Download and apply updates only once. When
205 not set, updates are continuously applied""")
206 group.add_argument('--no-index', action='store_false', dest='do_index',
207 help="""Do not index the new data. Only applicable
208 together with --once""")
212 params = ['update.php']
214 params.append('--init-updates')
215 if not args.update_functions:
216 params.apend('--no-update-functions')
217 elif args.check_for_updates:
218 params.append('--check-for-updates')
221 params.append('--import-osmosis')
223 params.append('--import-osmosis-all')
224 if not args.do_index:
225 params.append('--no-index')
227 return run_legacy_script(*params, nominatim_env=args)
232 Add additional data from a file or an online source.
234 Data is only imported, not indexed. You need to call `nominatim-update index`
235 to complete the process.
239 def add_args(parser):
240 group_name = parser.add_argument_group('Source')
241 group = group_name.add_mutually_exclusive_group(required=True)
242 group.add_argument('--file', metavar='FILE',
243 help='Import data from an OSM file')
244 group.add_argument('--diff', metavar='FILE',
245 help='Import data from an OSM diff file')
246 group.add_argument('--node', metavar='ID', type=int,
247 help='Import a single node from the API')
248 group.add_argument('--way', metavar='ID', type=int,
249 help='Import a single way from the API')
250 group.add_argument('--relation', metavar='ID', type=int,
251 help='Import a single relation from the API')
252 group.add_argument('--tiger-data', metavar='DIR',
253 help='Add housenumbers from the US TIGER census database.')
254 group = parser.add_argument_group('Extra arguments')
255 group.add_argument('--use-main-api', action='store_true',
256 help='Use OSM API instead of Overpass to download objects')
261 return run_legacy_script('setup.php', '--import-tiger-data', nominatim_env=args)
263 params = [ 'update.php']
265 params.extend(('--import-file', args.file))
267 params.extend(('--import-diff', args.diff))
269 params.extend(('--import-node', args.node))
271 params.extend(('--import-way', args.way))
273 params.extend(('--import-relation' , args.relation))
274 if args.use_main_api:
275 params.append('--use-main-api')
276 return run_legacy_script(*params, nominatim_env=args)
281 Reindex all new and modified data.
285 def add_args(parser):
290 return run_legacy_script('update.php', '--index', nominatim_env=args)
295 Recompute auxillary data used by the indexing process.
297 These functions must not be run in parallel with other update commands.
301 def add_args(parser):
302 group = parser.add_argument_group('Data arguments')
303 group.add_argument('--postcodes', action='store_true',
304 help='Update postcode centroid table')
305 group.add_argument('--word-counts', action='store_true',
306 help='Compute frequency of full-word search terms')
307 group.add_argument('--address-levels', action='store_true',
308 help='Reimport address level configuration')
309 group.add_argument('--importance', action='store_true',
310 help='Recompute place importances (expensive!)')
311 group.add_argument('--functions', action='store_true',
312 help='Update the PL/pgSQL functions in the database')
313 group.add_argument('--wiki-data', action='store_true',
314 help='Update Wikipedia/data importance numbers.')
315 group.add_argument('--website', action='store_true',
316 help='Refresh the directory that serves the scripts for the web API')
317 group = parser.add_argument_group('Arguments for function refresh')
318 group.add_argument('--no-diff-updates', action='store_false', dest='diffs',
319 help='Do not enable code for propagating updates')
320 group.add_argument('--enable-debug-statements', action='store_true',
321 help='Enable debug warning statements in functions')
326 run_legacy_script('update.php', '--calculate-postcodes',
327 nominatim_env=args, throw_on_fail=True)
329 run_legacy_script('update.php', '--recompute-word-counts',
330 nominatim_env=args, throw_on_fail=True)
331 if args.address_levels:
332 run_legacy_script('update.php', '--update-address-levels',
333 nominatim_env=args, throw_on_fail=True)
335 run_legacy_script('update.php', '--recompute-importance',
336 nominatim_env=args, throw_on_fail=True)
338 params = ['setup.php', '--create-functions', '--create-partition-functions']
340 params.append('--enable-diff-updates')
341 if args.enable_debug_statements:
342 params.append('--enable-debug-statements')
343 run_legacy_script(*params, nominatim_env=args, throw_on_fail=True)
345 run_legacy_script('setup.php', '--import-wikipedia-articles',
346 nominatim_env=args, throw_on_fail=True)
348 run_legacy_script('setup.php', '--setup-website',
349 nominatim_env=args, throw_on_fail=True)
352 class AdminCheckDatabase:
354 Check that the database is complete and operational.
358 def add_args(parser):
363 return run_legacy_script('check_import_finished.php', nominatim_env=args)
368 Warm database caches for search and reverse queries.
372 def add_args(parser):
373 group = parser.add_argument_group('Target arguments')
374 group.add_argument('--search-only', action='store_const', dest='target',
376 help="Only pre-warm tables for search queries")
377 group.add_argument('--reverse-only', action='store_const', dest='target',
379 help="Only pre-warm tables for reverse queries")
383 params = ['warm.php']
384 if args.target == 'reverse':
385 params.append('--reverse-only')
386 if args.target == 'search':
387 params.append('--search-only')
388 return run_legacy_script(*params, nominatim_env=args)
393 Export addresses as CSV file from a Nominatim database.
397 def add_args(parser):
398 group = parser.add_argument_group('Output arguments')
399 group.add_argument('--output-type', default='street',
400 choices=('continent', 'country', 'state', 'county',
401 'city', 'suburb', 'street', 'path'),
402 help='Type of places to output (default: street)')
403 group.add_argument('--output-format',
404 default='street;suburb;city;county;state;country',
405 help="""Semicolon-separated list of address types
406 (see --output-type). Multiple ranks can be
407 merged into one column by simply using a
408 comma-separated list.""")
409 group.add_argument('--output-all-postcodes', action='store_true',
410 help="""List all postcodes for address instead of
411 just the most likely one""")
412 group.add_argument('--language',
413 help="""Preferred language for output
414 (use local name, if omitted)""")
415 group = parser.add_argument_group('Filter arguments')
416 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
417 help='Export only objects within country')
418 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
419 help='Export only children of this OSM node')
420 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
421 help='Export only children of this OSM way')
422 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
423 help='Export only children of this OSM relation')
428 params = ['export.php',
429 '--output-type', args.output_type,
430 '--output-format', args.output_format]
431 if args.output_all_postcodes:
432 params.append('--output-all-postcodes')
434 params.extend(('--language', args.language))
435 if args.restrict_to_country:
436 params.extend(('--restrict-to-country', args.restrict_to_country))
437 if args.restrict_to_osm_node:
438 params.exted(('--restrict-to-osm-node', args.restrict_to_osm_node))
439 if args.restrict_to_osm_way:
440 params.exted(('--restrict-to-osm-way', args.restrict_to_osm_way))
441 if args.restrict_to_osm_relation:
442 params.exted(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
444 return run_legacy_script(*params, nominatim_env=args)
451 def add_args(parser):
455 print("TODO: searching")
458 def nominatim(**kwargs):
460 Command-line tools for importing, updating, administrating and
461 querying the Nominatim database.
463 parser = CommandlineParser('nominatim', nominatim.__doc__)
465 parser.add_subcommand('import', SetupAll)
466 parser.add_subcommand('freeze', SetupFreeze)
467 parser.add_subcommand('replication', UpdateReplication)
469 parser.add_subcommand('check-database', AdminCheckDatabase)
470 parser.add_subcommand('warm', AdminWarm)
472 parser.add_subcommand('special-phrases', SetupSpecialPhrases)
474 parser.add_subcommand('add-data', UpdateAddData)
475 parser.add_subcommand('index', UpdateIndex)
476 parser.add_subcommand('refresh', UpdateRefresh)
478 parser.add_subcommand('export', QueryExport)
479 parser.add_subcommand('search', QueryTodo)
480 parser.add_subcommand('reverse', QueryTodo)
481 parser.add_subcommand('lookup', QueryTodo)
482 parser.add_subcommand('details', QueryTodo)
483 parser.add_subcommand('status', QueryTodo)