]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/cli.py
consolidate cli interface to single tool
[nominatim.git] / nominatim / cli.py
1 """
2 Command-line interface to the Nominatim functions for import, update,
3 database administration and querying.
4 """
5 import sys
6 import argparse
7 import logging
8
9 class CommandlineParser:
10     """ Wraps some of the common functions for parsing the command line
11         and setting up subcommands.
12     """
13     def __init__(self, prog, description):
14         self.parser = argparse.ArgumentParser(
15             prog=prog,
16             description=description,
17             formatter_class=argparse.RawDescriptionHelpFormatter)
18
19         self.subs = self.parser.add_subparsers(title='available commands',
20                                                dest='subcommand')
21
22         # Arguments added to every sub-command
23         self.default_args = argparse.ArgumentParser(add_help=False)
24         group = self.default_args.add_argument_group('Default arguments')
25         group.add_argument('-h', '--help', action='help',
26                            help='Show this help message and exit')
27         group.add_argument('-q', '--quiet', action='store_const', const=0,
28                            dest='verbose', default=1,
29                            help='Print only error messages')
30         group.add_argument('-v', '--verbose', action='count', default=1,
31                            help='Increase verboseness of output')
32         group.add_argument('--project-dir', metavar='DIR',
33                            help='Base directory of the Nominatim installation (default:.)')
34         group.add_argument('-j', '--threads', metavar='NUM', type=int,
35                            help='Number of parallel threads to use')
36
37
38     def add_subcommand(self, name, cmd):
39         """ Add a subcommand to the parser. The subcommand must be a class
40             with a function add_args() that adds the parameters for the
41             subcommand and a run() function that executes the command.
42         """
43         parser = self.subs.add_parser(name, parents=[self.default_args],
44                                       help=cmd.__doc__.split('\n', 1)[0],
45                                       description=cmd.__doc__,
46                                       formatter_class=argparse.RawDescriptionHelpFormatter,
47                                       add_help=False)
48         parser.set_defaults(command=cmd)
49         cmd.add_args(parser)
50
51     def run(self):
52         """ Parse the command line arguments of the program and execute the
53             appropriate subcommand.
54         """
55         args = self.parser.parse_args()
56
57         if args.subcommand is None:
58             self.parser.print_help()
59         else:
60             logging.basicConfig(stream=sys.stderr,
61                                 format='%(asctime)s %(levelname)s: %(message)s',
62                                 datefmt='%Y-%m-%d %H:%M:%S',
63                                 level=max(4 - args.verbose, 1) * 10)
64             args.command.run(args)
65
66
67 class SetupAll:
68     """\
69     Create a new Nominatim database from an OSM file.
70     """
71
72     @staticmethod
73     def add_args(parser):
74         group_name = parser.add_argument_group('Required arguments')
75         group = group_name.add_mutually_exclusive_group(required=True)
76         group.add_argument('--osm-file',
77                            help='OSM file to be imported.')
78         group.add_argument('--continue', nargs=1,
79                            choices=['load-data', 'indexing', 'db-postprocess'],
80                            help='Continue an import that was interrupted')
81         group = parser.add_argument_group('Optional arguments')
82         group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
83                            help='Size of cache to be used by osm2pgsql (in MB)')
84         group.add_argument('--reverse-only', action='store_true',
85                            help='Do not create tables and indexes for searching')
86         group.add_argument('--enable-debug-statements', action='store_true',
87                            help='Include debug warning statements in SQL code')
88         group.add_argument('--no-partitions', action='store_true',
89                            help="""Do not partition search indices
90                                    (speeds up import of single country extracts)""")
91         group.add_argument('--no-updates', action='store_true',
92                            help="""Do not keep tables that are only needed for
93                                    updating the database later""")
94         group = parser.add_argument_group('Expert options')
95         group.add_argument('--ignore-errors', action='store_true',
96                            help='Continue import even when errors in SQL are present')
97         group.add_argument('--disable-token-precalc', action='store_true',
98                            help='Disable name precalculation')
99         group.add_argument('--index-noanalyse', action='store_true',
100                            help='Do not perform analyse operations during index')
101
102
103     @staticmethod
104     def run(args):
105         print("TODO: ./utils/setup.php", args)
106
107
108 class SetupFreeze:
109     """\
110     Make database read-only.
111
112     About half of data in the Nominatim database is kept only to be able to
113     keep the data up-to-date with new changes made in OpenStreetMap. This
114     command drops all this data and only keeps the part needed for geocoding
115     itself.
116
117     This command has the same effect as the `--no-updates` option for imports.
118     """
119
120     @staticmethod
121     def add_args(parser):
122         pass # No options
123
124     @staticmethod
125     def run(args):
126         print("TODO: setup drop", args)
127
128
129 class SetupSpecialPhrases:
130     """\
131     Maintain special phrases.
132     """
133
134     @staticmethod
135     def add_args(parser):
136         group = parser.add_argument_group('Input arguments')
137         group.add_argument('--from-wiki', action='store_true',
138                            help='Pull special phrases from the OSM wiki.')
139         group = parser.add_argument_group('Output arguments')
140         group.add_argument('-o', '--output', default='-',
141                            type=argparse.FileType('w', encoding='UTF-8'),
142                            help="""File to write the preprocessed phrases to.
143                                    If omitted, it will be written to stdout.""")
144
145     @staticmethod
146     def run(args):
147         print("./utils/specialphrases.php --from-wiki", args)
148
149
150 class UpdateReplication:
151     """\
152     Update the database using an online replication service.
153     """
154
155     @staticmethod
156     def add_args(parser):
157         group = parser.add_argument_group('Arguments for initialisation')
158         group.add_argument('--init', action='store_true',
159                            help='Initialise the update process')
160         group.add_argument('--no-update-functions', dest='update_functions',
161                            action='store_false',
162                            help="""Do not update the trigger function to
163                                    support differential updates.""")
164         group = parser.add_argument_group('Arguments for updates')
165         group.add_argument('--check-for-updates', action='store_true',
166                            help='Check if new updates are available and exit')
167         group.add_argument('--once', action='store_true',
168                            help="""Download and apply updates only once. When
169                                    not set, updates are continuously applied""")
170         group.add_argument('--no-index', action='store_false', dest='do_index',
171                            help="""Do not index the new data. Only applicable
172                                    together with --once""")
173
174     @staticmethod
175     def run(args):
176         if args.init:
177             print('./utils/update.php --init-updates', args)
178         else:
179             print('./utils/update.php --import-osmosis(-all)', args)
180
181
182 class UpdateAddData:
183     """\
184     Add additional data from a file or an online source.
185
186     Data is only imported, not indexed. You need to call `nominatim-update index`
187     to complete the process.
188     """
189
190     @staticmethod
191     def add_args(parser):
192         group_name = parser.add_argument_group('Source')
193         group = group_name.add_mutually_exclusive_group(required=True)
194         group.add_argument('--file', metavar='FILE',
195                            help='Import data from an OSM file')
196         group.add_argument('--diff', metavar='FILE',
197                            help='Import data from an OSM diff file')
198         group.add_argument('--node', metavar='ID', type=int,
199                            help='Import a single node from the API')
200         group.add_argument('--way', metavar='ID', type=int,
201                            help='Import a single way from the API')
202         group.add_argument('--relation', metavar='ID', type=int,
203                            help='Import a single relation from the API')
204         group.add_argument('--tiger-data', metavar='DIR',
205                            help='Add housenumbers from the US TIGER census database.')
206         group = parser.add_argument_group('Extra arguments')
207         group.add_argument('--use-main-api', action='store_true',
208                            help='Use OSM API instead of Overpass to download objects')
209
210     @staticmethod
211     def run(args):
212         print('./utils/update.php --import-*', args)
213
214
215 class UpdateIndex:
216     """\
217     Reindex all new and modified data.
218     """
219
220     @staticmethod
221     def add_args(parser):
222         pass
223
224     @staticmethod
225     def run(args):
226         print('./utils/update.php --index', args)
227
228
229 class UpdateRefresh:
230     """\
231     Recompute auxillary data used by the indexing process.
232
233     These functions must not be run in parallel with other update commands.
234     """
235
236     @staticmethod
237     def add_args(parser):
238         group = parser.add_argument_group('Data arguments')
239         group.add_argument('--postcodes', action='store_true',
240                            help='Update postcode centroid table')
241         group.add_argument('--word-counts', action='store_true',
242                            help='Compute frequency of full-word search terms')
243         group.add_argument('--address-levels', action='store_true',
244                            help='Reimport address level configuration')
245         group.add_argument('--importance', action='store_true',
246                            help='Recompute place importances')
247         group.add_argument('--functions', action='store_true',
248                            help='Update the PL/pgSQL functions in the database')
249         group.add_argument('--wiki-data',
250                            help='Update Wikipedia/data importance numbers.')
251         group.add_argument('--website', action='store_true',
252                            help='Refresh the directory that serves the scripts for the web API')
253         group = parser.add_argument_group('Arguments for function refresh')
254         group.add_argument('--no-diff-updates', action='store_false', dest='diffs',
255                            help='Do not enable code for propagating updates')
256
257     @staticmethod
258     def run(args):
259         print('./utils/update.php', args)
260
261
262 class AdminCheckDatabase:
263     """\
264     Check that the database is complete and operational.
265     """
266
267     @staticmethod
268     def add_args(parser):
269         pass # No options
270
271     @staticmethod
272     def run(args):
273         print("TODO: ./utils/check_import_finished.php", args)
274
275
276 class AdminWarm:
277     """\
278     Warm database caches for search and reverse queries.
279     """
280
281     @staticmethod
282     def add_args(parser):
283         group = parser.add_argument_group('Target arguments')
284         group.add_argument('--search-only', action='store_const', dest='target',
285                            const='search',
286                            help="Only pre-warm tables for search queries")
287         group.add_argument('--reverse-only', action='store_const', dest='target',
288                            const='reverse',
289                            help="Only pre-warm tables for reverse queries")
290
291     @staticmethod
292     def run(args):
293         print("TODO: ./utils/warm.php", args)
294
295
296 class QueryExport:
297     """\
298     Export addresses as CSV file from a Nominatim database.
299     """
300
301     @staticmethod
302     def add_args(parser):
303         group = parser.add_argument_group('Output arguments')
304         group.add_argument('--output-type', default='street',
305                            choices=('continent', 'country', 'state', 'county',
306                                     'city', 'suburb', 'street', 'path'),
307                            help='Type of places to output (default: street)')
308         group.add_argument('--output-format',
309                            default='street;suburb;city;county;state;country',
310                            help="""Semicolon-separated list of address types
311                                    (see --output-type). Multiple ranks can be
312                                    merged into one column by simply using a
313                                    comma-separated list.""")
314         group.add_argument('--output-all-postcodes', action='store_true',
315                            help="""List all postcodes for address instead of
316                                    just the most likely one""")
317         group.add_argument('--language',
318                            help="""Preferred language for output
319                                    (use local name, if omitted)""")
320         group = parser.add_argument_group('Filter arguments')
321         group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
322                            help='Export only objects within country')
323         group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
324                            help='Export only children of this OSM node')
325         group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
326                            help='Export only children of this OSM way')
327         group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
328                            help='Export only children of this OSM relation')
329
330
331     @staticmethod
332     def run(args):
333         print("TODO: ./utils/export.php", args)
334
335
336 class QueryTodo:
337     """\
338     Todo
339     """
340     @staticmethod
341     def add_args(parser):
342         pass
343
344     def run(args):
345         print("TODO: searching")
346
347
348 def nominatim(**kwargs):
349     """\
350     Command-line tools for importing, updating, administrating and
351     querying the Nominatim database.
352     """
353     parser = CommandlineParser('nominatim', nominatim.__doc__)
354
355     parser.add_subcommand('import', SetupAll)
356     parser.add_subcommand('freeze', SetupFreeze)
357     parser.add_subcommand('replication', UpdateReplication)
358
359     parser.add_subcommand('check-database', AdminCheckDatabase)
360     parser.add_subcommand('warm', AdminWarm)
361
362     parser.add_subcommand('special-phrases', SetupSpecialPhrases)
363
364     parser.add_subcommand('add-data', UpdateAddData)
365     parser.add_subcommand('index', UpdateIndex)
366     parser.add_subcommand('refresh', UpdateRefresh)
367
368     parser.add_subcommand('export', QueryExport)
369     parser.add_subcommand('search', QueryTodo)
370     parser.add_subcommand('reverse', QueryTodo)
371     parser.add_subcommand('lookup', QueryTodo)
372     parser.add_subcommand('details', QueryTodo)
373     parser.add_subcommand('status', QueryTodo)
374
375     parser.run()