2 Command-line interface to the Nominatim functions for import, update,
3 database administration and querying.
9 from pathlib import Path
11 from nominatim.config import Configuration
12 from nominatim.tools.exec_utils import run_legacy_script, run_php_server
13 from nominatim.errors import UsageError
14 from nominatim import clicmd
15 from nominatim.clicmd.args import NominatimArgs
17 LOG = logging.getLogger()
20 class CommandlineParser:
21 """ Wraps some of the common functions for parsing the command line
22 and setting up subcommands.
24 def __init__(self, prog, description):
25 self.parser = argparse.ArgumentParser(
27 description=description,
28 formatter_class=argparse.RawDescriptionHelpFormatter)
30 self.subs = self.parser.add_subparsers(title='available commands',
33 # Arguments added to every sub-command
34 self.default_args = argparse.ArgumentParser(add_help=False)
35 group = self.default_args.add_argument_group('Default arguments')
36 group.add_argument('-h', '--help', action='help',
37 help='Show this help message and exit')
38 group.add_argument('-q', '--quiet', action='store_const', const=0,
39 dest='verbose', default=1,
40 help='Print only error messages')
41 group.add_argument('-v', '--verbose', action='count', default=1,
42 help='Increase verboseness of output')
43 group.add_argument('--project-dir', metavar='DIR', default='.',
44 help='Base directory of the Nominatim installation (default:.)')
45 group.add_argument('-j', '--threads', metavar='NUM', type=int,
46 help='Number of parallel threads to use')
49 def add_subcommand(self, name, cmd):
50 """ Add a subcommand to the parser. The subcommand must be a class
51 with a function add_args() that adds the parameters for the
52 subcommand and a run() function that executes the command.
54 parser = self.subs.add_parser(name, parents=[self.default_args],
55 help=cmd.__doc__.split('\n', 1)[0],
56 description=cmd.__doc__,
57 formatter_class=argparse.RawDescriptionHelpFormatter,
59 parser.set_defaults(command=cmd)
62 def run(self, **kwargs):
63 """ Parse the command line arguments of the program and execute the
64 appropriate subcommand.
66 args = NominatimArgs()
67 self.parser.parse_args(args=kwargs.get('cli_args'), namespace=args)
69 if args.subcommand is None:
70 self.parser.print_help()
73 for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'sqllib_dir',
74 'data_dir', 'config_dir', 'phpcgi_path'):
75 setattr(args, arg, Path(kwargs[arg]))
76 args.project_dir = Path(args.project_dir).resolve()
78 if 'cli_args' not in kwargs:
79 logging.basicConfig(stream=sys.stderr,
80 format='%(asctime)s: %(message)s',
81 datefmt='%Y-%m-%d %H:%M:%S',
82 level=max(4 - args.verbose, 1) * 10)
84 args.config = Configuration(args.project_dir, args.config_dir,
85 environ=kwargs.get('environ', os.environ))
86 args.config.set_libdirs(module=args.module_dir,
87 osm2pgsql=args.osm2pgsql_path,
92 log = logging.getLogger()
93 log.warning('Using project directory: %s', str(args.project_dir))
96 return args.command.run(args)
97 except UsageError as exception:
98 if log.isEnabledFor(logging.DEBUG):
99 raise # use Python's exception printing
100 log.fatal('FATAL: %s', exception)
102 # If we get here, then execution has failed in some way.
106 ##### Subcommand classes
108 # Each class needs to implement two functions: add_args() adds the CLI parameters
109 # for the subfunction, run() executes the subcommand.
111 # The class documentation doubles as the help text for the command. The
112 # first line is also used in the summary when calling the program without
115 # No need to document the functions each time.
116 # pylint: disable=C0111
117 # Using non-top-level imports to make pyosmium optional for replication only.
118 # pylint: disable=E0012,C0415
121 Add additional data from a file or an online source.
123 Data is only imported, not indexed. You need to call `nominatim index`
124 to complete the process.
128 def add_args(parser):
129 group_name = parser.add_argument_group('Source')
130 group = group_name.add_mutually_exclusive_group(required=True)
131 group.add_argument('--file', metavar='FILE',
132 help='Import data from an OSM file')
133 group.add_argument('--diff', metavar='FILE',
134 help='Import data from an OSM diff file')
135 group.add_argument('--node', metavar='ID', type=int,
136 help='Import a single node from the API')
137 group.add_argument('--way', metavar='ID', type=int,
138 help='Import a single way from the API')
139 group.add_argument('--relation', metavar='ID', type=int,
140 help='Import a single relation from the API')
141 group.add_argument('--tiger-data', metavar='DIR',
142 help='Add housenumbers from the US TIGER census database.')
143 group = parser.add_argument_group('Extra arguments')
144 group.add_argument('--use-main-api', action='store_true',
145 help='Use OSM API instead of Overpass to download objects')
149 from nominatim.tokenizer import factory as tokenizer_factory
150 from nominatim.tools import tiger_data
153 tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
154 return tiger_data.add_tiger_data(args.tiger_data,
155 args.config, args.threads or 1,
158 params = ['update.php']
160 params.extend(('--import-file', args.file))
162 params.extend(('--import-diff', args.diff))
164 params.extend(('--import-node', args.node))
166 params.extend(('--import-way', args.way))
168 params.extend(('--import-relation', args.relation))
169 if args.use_main_api:
170 params.append('--use-main-api')
171 return run_legacy_script(*params, nominatim_env=args)
176 Export addresses as CSV file from the database.
180 def add_args(parser):
181 group = parser.add_argument_group('Output arguments')
182 group.add_argument('--output-type', default='street',
183 choices=('continent', 'country', 'state', 'county',
184 'city', 'suburb', 'street', 'path'),
185 help='Type of places to output (default: street)')
186 group.add_argument('--output-format',
187 default='street;suburb;city;county;state;country',
188 help=("Semicolon-separated list of address types "
189 "(see --output-type). Multiple ranks can be "
190 "merged into one column by simply using a "
191 "comma-separated list."))
192 group.add_argument('--output-all-postcodes', action='store_true',
193 help=("List all postcodes for address instead of "
194 "just the most likely one"))
195 group.add_argument('--language',
196 help=("Preferred language for output "
197 "(use local name, if omitted)"))
198 group = parser.add_argument_group('Filter arguments')
199 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
200 help='Export only objects within country')
201 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
202 help='Export only children of this OSM node')
203 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
204 help='Export only children of this OSM way')
205 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
206 help='Export only children of this OSM relation')
211 params = ['export.php',
212 '--output-type', args.output_type,
213 '--output-format', args.output_format]
214 if args.output_all_postcodes:
215 params.append('--output-all-postcodes')
217 params.extend(('--language', args.language))
218 if args.restrict_to_country:
219 params.extend(('--restrict-to-country', args.restrict_to_country))
220 if args.restrict_to_osm_node:
221 params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
222 if args.restrict_to_osm_way:
223 params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
224 if args.restrict_to_osm_relation:
225 params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
227 return run_legacy_script(*params, nominatim_env=args)
232 Start a simple web server for serving the API.
234 This command starts the built-in PHP webserver to serve the website
235 from the current project directory. This webserver is only suitable
236 for testing and develop. Do not use it in production setups!
238 By the default, the webserver can be accessed at: http://127.0.0.1:8088
242 def add_args(parser):
243 group = parser.add_argument_group('Server arguments')
244 group.add_argument('--server', default='127.0.0.1:8088',
245 help='The address the server will listen to.')
249 run_php_server(args.server, args.project_dir / 'website')
251 def get_set_parser(**kwargs):
253 Initializes the parser and adds various subcommands for
256 parser = CommandlineParser('nominatim', nominatim.__doc__)
258 parser.add_subcommand('import', clicmd.SetupAll)
259 parser.add_subcommand('freeze', clicmd.SetupFreeze)
260 parser.add_subcommand('replication', clicmd.UpdateReplication)
262 parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases)
264 parser.add_subcommand('add-data', UpdateAddData)
265 parser.add_subcommand('index', clicmd.UpdateIndex)
266 parser.add_subcommand('refresh', clicmd.UpdateRefresh)
268 parser.add_subcommand('admin', clicmd.AdminFuncs)
270 parser.add_subcommand('export', QueryExport)
271 parser.add_subcommand('serve', AdminServe)
273 if kwargs.get('phpcgi_path'):
274 parser.add_subcommand('search', clicmd.APISearch)
275 parser.add_subcommand('reverse', clicmd.APIReverse)
276 parser.add_subcommand('lookup', clicmd.APILookup)
277 parser.add_subcommand('details', clicmd.APIDetails)
278 parser.add_subcommand('status', clicmd.APIStatus)
280 parser.parser.epilog = 'php-cgi not found. Query commands not available.'
285 def nominatim(**kwargs):
287 Command-line tools for importing, updating, administrating and
288 querying the Nominatim database.
290 parser = get_set_parser(**kwargs)
292 return parser.run(**kwargs)