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
16 from nominatim.tools import tiger_data
18 LOG = logging.getLogger()
21 class CommandlineParser:
22 """ Wraps some of the common functions for parsing the command line
23 and setting up subcommands.
25 def __init__(self, prog, description):
26 self.parser = argparse.ArgumentParser(
28 description=description,
29 formatter_class=argparse.RawDescriptionHelpFormatter)
31 self.subs = self.parser.add_subparsers(title='available commands',
34 # Arguments added to every sub-command
35 self.default_args = argparse.ArgumentParser(add_help=False)
36 group = self.default_args.add_argument_group('Default arguments')
37 group.add_argument('-h', '--help', action='help',
38 help='Show this help message and exit')
39 group.add_argument('-q', '--quiet', action='store_const', const=0,
40 dest='verbose', default=1,
41 help='Print only error messages')
42 group.add_argument('-v', '--verbose', action='count', default=1,
43 help='Increase verboseness of output')
44 group.add_argument('--project-dir', metavar='DIR', default='.',
45 help='Base directory of the Nominatim installation (default:.)')
46 group.add_argument('-j', '--threads', metavar='NUM', type=int,
47 help='Number of parallel threads to use')
50 def add_subcommand(self, name, cmd):
51 """ Add a subcommand to the parser. The subcommand must be a class
52 with a function add_args() that adds the parameters for the
53 subcommand and a run() function that executes the command.
55 parser = self.subs.add_parser(name, parents=[self.default_args],
56 help=cmd.__doc__.split('\n', 1)[0],
57 description=cmd.__doc__,
58 formatter_class=argparse.RawDescriptionHelpFormatter,
60 parser.set_defaults(command=cmd)
63 def run(self, **kwargs):
64 """ Parse the command line arguments of the program and execute the
65 appropriate subcommand.
67 args = NominatimArgs()
68 self.parser.parse_args(args=kwargs.get('cli_args'), namespace=args)
70 if args.subcommand is None:
71 self.parser.print_help()
74 for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'sqllib_dir',
75 'data_dir', 'config_dir', 'phpcgi_path'):
76 setattr(args, arg, Path(kwargs[arg]))
77 args.project_dir = Path(args.project_dir).resolve()
79 if 'cli_args' not in kwargs:
80 logging.basicConfig(stream=sys.stderr,
81 format='%(asctime)s: %(message)s',
82 datefmt='%Y-%m-%d %H:%M:%S',
83 level=max(4 - args.verbose, 1) * 10)
85 args.config = Configuration(args.project_dir, args.config_dir,
86 environ=kwargs.get('environ', os.environ))
87 args.config.set_libdirs(module=args.module_dir,
88 osm2pgsql=args.osm2pgsql_path,
93 log = logging.getLogger()
94 log.warning('Using project directory: %s', str(args.project_dir))
97 return args.command.run(args)
98 except UsageError as exception:
99 if log.isEnabledFor(logging.DEBUG):
100 raise # use Python's exception printing
101 log.fatal('FATAL: %s', exception)
103 # If we get here, then execution has failed in some way.
107 ##### Subcommand classes
109 # Each class needs to implement two functions: add_args() adds the CLI parameters
110 # for the subfunction, run() executes the subcommand.
112 # The class documentation doubles as the help text for the command. The
113 # first line is also used in the summary when calling the program without
116 # No need to document the functions each time.
117 # pylint: disable=C0111
118 # Using non-top-level imports to make pyosmium optional for replication only.
119 # pylint: disable=E0012,C0415
122 Add additional data from a file or an online source.
124 Data is only imported, not indexed. You need to call `nominatim-update index`
125 to complete the process.
129 def add_args(parser):
130 group_name = parser.add_argument_group('Source')
131 group = group_name.add_mutually_exclusive_group(required=True)
132 group.add_argument('--file', metavar='FILE',
133 help='Import data from an OSM file')
134 group.add_argument('--diff', metavar='FILE',
135 help='Import data from an OSM diff file')
136 group.add_argument('--node', metavar='ID', type=int,
137 help='Import a single node from the API')
138 group.add_argument('--way', metavar='ID', type=int,
139 help='Import a single way from the API')
140 group.add_argument('--relation', metavar='ID', type=int,
141 help='Import a single relation from the API')
142 group.add_argument('--tiger-data', metavar='DIR',
143 help='Add housenumbers from the US TIGER census database.')
144 group = parser.add_argument_group('Extra arguments')
145 group.add_argument('--use-main-api', action='store_true',
146 help='Use OSM API instead of Overpass to download objects')
151 return tiger_data.add_tiger_data(args.config.get_libpq_dsn(),
157 params = ['update.php']
159 params.extend(('--import-file', args.file))
161 params.extend(('--import-diff', args.diff))
163 params.extend(('--import-node', args.node))
165 params.extend(('--import-way', args.way))
167 params.extend(('--import-relation', args.relation))
168 if args.use_main_api:
169 params.append('--use-main-api')
170 return run_legacy_script(*params, nominatim_env=args)
175 Export addresses as CSV file from the database.
179 def add_args(parser):
180 group = parser.add_argument_group('Output arguments')
181 group.add_argument('--output-type', default='street',
182 choices=('continent', 'country', 'state', 'county',
183 'city', 'suburb', 'street', 'path'),
184 help='Type of places to output (default: street)')
185 group.add_argument('--output-format',
186 default='street;suburb;city;county;state;country',
187 help=("Semicolon-separated list of address types "
188 "(see --output-type). Multiple ranks can be "
189 "merged into one column by simply using a "
190 "comma-separated list."))
191 group.add_argument('--output-all-postcodes', action='store_true',
192 help=("List all postcodes for address instead of "
193 "just the most likely one"))
194 group.add_argument('--language',
195 help=("Preferred language for output "
196 "(use local name, if omitted)"))
197 group = parser.add_argument_group('Filter arguments')
198 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
199 help='Export only objects within country')
200 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
201 help='Export only children of this OSM node')
202 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
203 help='Export only children of this OSM way')
204 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
205 help='Export only children of this OSM relation')
210 params = ['export.php',
211 '--output-type', args.output_type,
212 '--output-format', args.output_format]
213 if args.output_all_postcodes:
214 params.append('--output-all-postcodes')
216 params.extend(('--language', args.language))
217 if args.restrict_to_country:
218 params.extend(('--restrict-to-country', args.restrict_to_country))
219 if args.restrict_to_osm_node:
220 params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
221 if args.restrict_to_osm_way:
222 params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
223 if args.restrict_to_osm_relation:
224 params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
226 return run_legacy_script(*params, nominatim_env=args)
231 Start a simple web server for serving the API.
233 This command starts the built-in PHP webserver to serve the website
234 from the current project directory. This webserver is only suitable
235 for testing and develop. Do not use it in production setups!
237 By the default, the webserver can be accessed at: http://127.0.0.1:8088
241 def add_args(parser):
242 group = parser.add_argument_group('Server arguments')
243 group.add_argument('--server', default='127.0.0.1:8088',
244 help='The address the server will listen to.')
248 run_php_server(args.server, args.project_dir / 'website')
250 def get_set_parser(**kwargs):
252 Initializes the parser and adds various subcommands for
255 parser = CommandlineParser('nominatim', nominatim.__doc__)
257 parser.add_subcommand('import', clicmd.SetupAll)
258 parser.add_subcommand('freeze', clicmd.SetupFreeze)
259 parser.add_subcommand('replication', clicmd.UpdateReplication)
261 parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases)
263 parser.add_subcommand('add-data', UpdateAddData)
264 parser.add_subcommand('index', clicmd.UpdateIndex)
265 parser.add_subcommand('refresh', clicmd.UpdateRefresh)
267 parser.add_subcommand('admin', clicmd.AdminFuncs)
269 parser.add_subcommand('export', QueryExport)
270 parser.add_subcommand('serve', AdminServe)
272 if kwargs.get('phpcgi_path'):
273 parser.add_subcommand('search', clicmd.APISearch)
274 parser.add_subcommand('reverse', clicmd.APIReverse)
275 parser.add_subcommand('lookup', clicmd.APILookup)
276 parser.add_subcommand('details', clicmd.APIDetails)
277 parser.add_subcommand('status', clicmd.APIStatus)
279 parser.parser.epilog = 'php-cgi not found. Query commands not available.'
284 def nominatim(**kwargs):
286 Command-line tools for importing, updating, administrating and
287 querying the Nominatim database.
289 parser = get_set_parser(**kwargs)
291 return parser.run(**kwargs)