]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/tools.py
2b86a5e67d461c711044ff1aa7bdb3418693d67a
[nominatim.git] / nominatim / tools.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 database and import data from an OSM file.
70     """
71
72     @staticmethod
73     def add_args(parser):
74         group = parser.add_argument_group('Required arguments')
75         group.add_argument('--osm-file', required=True,
76                            help='OSM file to be imported.')
77         group = parser.add_argument_group('Optional arguments')
78         group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
79                            help='Size of cache to be used by osm2pgsql (in MB)')
80         group.add_argument('--reverse-only', action='store_true',
81                            help='Do not create tables and indexes for searching')
82         group.add_argument('--enable-debug-statements', action='store_true',
83                            help='Include debug warning statements in SQL code')
84         group.add_argument('--no-partitions', action='store_true',
85                            help="""Do not partition search indices
86                                    (speeds up import of single country extracts)""")
87         group.add_argument('--no-updates', action='store_true',
88                            help="""Do not keep tables that are only needed for
89                                    updating the database later""")
90         group = parser.add_argument_group('Expert options')
91         group.add_argument('--ignore-errors', action='store_true',
92                            help='Continue import even when errors in SQL are present')
93         group.add_argument('--disable-token-precalc', action='store_true',
94                            help='Disable name precalculation')
95         group.add_argument('--index-noanalyse', action='store_true',
96                            help='Do not perform analyse operations during index')
97
98
99     @staticmethod
100     def run(args):
101         print("TODO: setup all", args)
102
103
104 class SetupContinue:
105     """\
106     Continue an import previously started with the `all` command.
107     """
108
109     @staticmethod
110     def add_args(parser):
111         group = parser.add_argument_group('Required aruments')
112         group.add_argument('pickup-point', nargs=1,
113                            choices=['load-data', 'indexing', 'db-postprocess'],
114                            help='Position where to continue the import')
115
116     @staticmethod
117     def run(args):
118         print("TODO: setup continue", args)
119
120 class SetupDrop:
121     """\
122     Remove all tables only needed for keeping data up-to-date.
123
124     About half of data in the Nominatim database is kept only to be able to
125     keep the data up-to-date with new changes made in OpenStreetMap. This
126     command drops all this data and only keeps the part needed for geocoding
127     itself.
128
129     This command has the same effect as the `--no-updates` option for imports.
130     """
131
132     @staticmethod
133     def add_args(parser):
134         pass # No options
135
136     @staticmethod
137     def run(args):
138         print("TODO: setup drop", args)
139
140 class SetupAddExternal:
141     """\
142     Add additional external data to the Nominatim database.
143     """
144
145     @staticmethod
146     def add_args(parser):
147         group = parser.add_argument_group('Data sources')
148         group.add_argument('--tiger-data', metavar='DIR',
149                            help='Add housenumbers from the US TIGER census database.')
150         group.add_argument('--wiki-data',
151                            help='Add or update Wikipedia/data importance numbers.')
152
153     @staticmethod
154     def run(args):
155         print("TODO: setup extern", args)
156
157
158 class SetupSpecialPhrases:
159     """\
160     Create special phrases.
161     """
162
163     @staticmethod
164     def add_args(parser):
165         group = parser.add_argument_group('Input arguments')
166         group.add_argument('--from-wiki', action='store_true',
167                            help='Pull special phrases from the OSM wiki.')
168         group = parser.add_argument_group('Output arguments')
169         group.add_argument('-o', '--output', default='-',
170                            type=argparse.FileType('w', encoding='UTF-8'),
171                            help="""File to write the preprocessed phrases to.
172                                    If omitted, it will be written to stdout.""")
173
174     @staticmethod
175     def run(args):
176         print("./utils/specialphrases.php --from-wiki", args)
177
178
179 class UpdateStatus:
180     """\
181     Check for the status of the data.
182     """
183
184     @staticmethod
185     def add_args(parser):
186         group = parser.add_argument_group('Additional arguments')
187         group.add_argument('--check-for-updates', action='store_true',
188                            help='Check if new updates are available')
189
190
191     @staticmethod
192     def run(args):
193         print('./utils/update.php --check-for-updates', args)
194
195
196 class UpdateReplication:
197     """\
198     Update the database using an online replication service.
199     """
200
201     @staticmethod
202     def add_args(parser):
203         group = parser.add_argument_group('Arguments for initialisation')
204         group.add_argument('--init', action='store_true',
205                            help='Initialise the update process')
206         group.add_argument('--no-update-functions', dest='update_functions',
207                            action='store_false',
208                            help="""Do not update the trigger function to
209                                    support differential updates.""")
210         group = parser.add_argument_group('Arguments for updates')
211         group.add_argument('--once', action='store_true',
212                            help="""Download and apply updates only once. When
213                                    not set, updates are continuously applied""")
214         group.add_argument('--no-index', action='store_false', dest='do_index',
215                            help="""Do not index the new data. Only applicable
216                                    together with --once""")
217
218     @staticmethod
219     def run(args):
220         if args.init:
221             print('./utils/update.php --init-updates', args)
222         else:
223             print('./utils/update.php --import-osmosis(-all)', args)
224
225
226 class UpdateImport:
227     """\
228     Add additional data from a file or an online source.
229
230     Data is only imported, not indexed. You need to call `nominatim-update index`
231     to complete the process.
232     """
233
234     @staticmethod
235     def add_args(parser):
236         group_name = parser.add_argument_group('Source')
237         group = group_name.add_mutually_exclusive_group(required=True)
238         group.add_argument('--file', metavar='FILE',
239                            help='Import data from an OSM file')
240         group.add_argument('--diff', metavar='FILE',
241                            help='Import data from an OSM diff file')
242         group.add_argument('--node', metavar='ID', type=int,
243                            help='Import a single node from the API')
244         group.add_argument('--way', metavar='ID', type=int,
245                            help='Import a single way from the API')
246         group.add_argument('--relation', metavar='ID', type=int,
247                            help='Import a single relation from the API')
248         group = parser.add_argument_group('Extra arguments')
249         group.add_argument('--use-main-api', action='store_true',
250                            help='Use OSM API instead of Overpass to download objects')
251
252     @staticmethod
253     def run(args):
254         print('./utils/update.php --import-*', args)
255
256
257 class UpdateIndex:
258     """\
259     Reindex all new and modified data.
260     """
261
262     @staticmethod
263     def add_args(parser):
264         pass
265
266     @staticmethod
267     def run(args):
268         print('./utils/update.php --index', args)
269
270
271 class UpdateRefresh:
272     """\
273     Recompute auxillary data used by the indexing process.
274
275     These functions must not be run in parallel with other update commands.
276     """
277
278     @staticmethod
279     def add_args(parser):
280         group = parser.add_argument_group('Data arguments')
281         group.add_argument('--postcodes', action='store_true',
282                            help='Update postcode centroid table')
283         group.add_argument('--word-counts', action='store_true',
284                            help='Compute frequency of full-word search terms')
285         group.add_argument('--address-levels', action='store_true',
286                            help='Reimport address level configuration')
287         group.add_argument('--importance', action='store_true',
288                            help='Recompute place importances')
289
290     @staticmethod
291     def run(args):
292         print('./utils/update.php', args)
293
294
295
296 class AdminCreateFunctions:
297     """\
298     Update the PL/pgSQL functions in the database.
299     """
300
301     @staticmethod
302     def add_args(parser):
303         group = parser.add_argument_group('Expert arguments')
304         group.add_argument('--no-diff-updates', action='store_false', dest='diffs',
305                            help='Do not enable code for propagating updates')
306
307     @staticmethod
308     def run(args):
309         print("TODO: ./utils/setup.php --create-functions --enable-diff-updates "
310               "--create-partition-functions", args)
311
312
313 class AdminSetupWebsite:
314     """\
315     Setup the directory that serves the scripts for the web API.
316
317     The directory is created under `/website` in the project directory.
318     """
319
320     @staticmethod
321     def add_args(parser):
322         pass # No options
323
324     @staticmethod
325     def run(args):
326         print("TODO: ./utils/setup.php --setup-website", args)
327
328
329 class AdminCheckDatabase:
330     """\
331     Check that the Nominatim database is complete and operational.
332     """
333
334     @staticmethod
335     def add_args(parser):
336         pass # No options
337
338     @staticmethod
339     def run(args):
340         print("TODO: ./utils/check_import_finished.php", args)
341
342
343 class AdminWarm:
344     """\
345     Pre-warm caches of the database for search and reverse queries.
346     """
347
348     @staticmethod
349     def add_args(parser):
350         group = parser.add_argument_group('Target arguments')
351         group.add_argument('--search-only', action='store_const', dest='target',
352                            const='search',
353                            help="Only pre-warm tables for search queries")
354         group.add_argument('--reverse-only', action='store_const', dest='target',
355                            const='reverse',
356                            help="Only pre-warm tables for reverse queries")
357
358     @staticmethod
359     def run(args):
360         print("TODO: ./utils/warm.php", args)
361
362
363 class AdminExport:
364     """\
365     Export addresses as CSV file from a Nominatim database
366     """
367
368     @staticmethod
369     def add_args(parser):
370         group = parser.add_argument_group('Output arguments')
371         group.add_argument('--output-type', default='street',
372                            choices=('continent', 'country', 'state', 'county',
373                                     'city', 'suburb', 'street', 'path'),
374                            help='Type of places to output (default: street)')
375         group.add_argument('--output-format',
376                            default='street;suburb;city;county;state;country',
377                            help="""Semicolon-separated list of address types
378                                    (see --output-type). Multiple ranks can be
379                                    merged into one column by simply using a
380                                    comma-separated list.""")
381         group.add_argument('--output-all-postcodes', action='store_true',
382                            help="""List all postcodes for address instead of
383                                    just the most likely one""")
384         group.add_argument('--language',
385                            help="""Preferred language for output
386                                    (use local name, if omitted)""")
387         group = parser.add_argument_group('Filter arguments')
388         group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
389                            help='Export only objects within country')
390         group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
391                            help='Export only children of this OSM node')
392         group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
393                            help='Export only children of this OSM way')
394         group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
395                            help='Export only children of this OSM relation')
396
397
398     @staticmethod
399     def run(args):
400         print("TODO: ./utils/export.php", args)
401
402 def setup(**kwargs):
403     """\
404     Commands for creating a Nominatim database and importing data.
405     """
406     parser = CommandlineParser('nominatim-setup', setup.__doc__)
407
408     parser.add_subcommand('all', SetupAll)
409     parser.add_subcommand('continue', SetupContinue())
410     parser.add_subcommand('drop', SetupDrop())
411     parser.add_subcommand('add-external', SetupAddExternal())
412     parser.add_subcommand('special-phrases', SetupSpecialPhrases())
413     parser.run()
414
415 def update(**kwargs):
416     """\
417     Commands for updating data inside a Nominatim database.
418     """
419     parser = CommandlineParser('nominatim-update', update.__doc__)
420
421     parser.add_subcommand('status', UpdateStatus())
422     parser.add_subcommand('replication', UpdateReplication())
423     parser.add_subcommand('import', UpdateImport())
424     parser.add_subcommand('index', UpdateIndex())
425     parser.add_subcommand('refresh', UpdateRefresh())
426
427     parser.run()
428
429 def admin(**kwargs):
430     """\
431     Commands for inspecting and maintaining a Nomiantim database.
432     """
433     parser = CommandlineParser('nominatim-admin', admin.__doc__)
434
435     parser.add_subcommand('create-functions', AdminCreateFunctions())
436     parser.add_subcommand('setup-website', AdminSetupWebsite())
437     parser.add_subcommand('check-database', AdminCheckDatabase())
438     parser.add_subcommand('warm', AdminWarm())
439     parser.add_subcommand('export', AdminExport())
440
441     parser.run()
442
443 def query(**kwargs):
444     """\
445     Query the database.
446
447     This provides a command-line query interface to Nominatim's API.
448     """
449     parser = CommandlineParser('nominatim-query', query.__doc__)
450
451     parser.run()