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.
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
119 Export addresses as CSV file from the database.
123 def add_args(parser):
124 group = parser.add_argument_group('Output arguments')
125 group.add_argument('--output-type', default='street',
126 choices=('continent', 'country', 'state', 'county',
127 'city', 'suburb', 'street', 'path'),
128 help='Type of places to output (default: street)')
129 group.add_argument('--output-format',
130 default='street;suburb;city;county;state;country',
131 help=("Semicolon-separated list of address types "
132 "(see --output-type). Multiple ranks can be "
133 "merged into one column by simply using a "
134 "comma-separated list."))
135 group.add_argument('--output-all-postcodes', action='store_true',
136 help=("List all postcodes for address instead of "
137 "just the most likely one"))
138 group.add_argument('--language',
139 help=("Preferred language for output "
140 "(use local name, if omitted)"))
141 group = parser.add_argument_group('Filter arguments')
142 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
143 help='Export only objects within country')
144 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
145 help='Export only children of this OSM node')
146 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
147 help='Export only children of this OSM way')
148 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
149 help='Export only children of this OSM relation')
154 params = ['export.php',
155 '--output-type', args.output_type,
156 '--output-format', args.output_format]
157 if args.output_all_postcodes:
158 params.append('--output-all-postcodes')
160 params.extend(('--language', args.language))
161 if args.restrict_to_country:
162 params.extend(('--restrict-to-country', args.restrict_to_country))
163 if args.restrict_to_osm_node:
164 params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
165 if args.restrict_to_osm_way:
166 params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
167 if args.restrict_to_osm_relation:
168 params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
170 return run_legacy_script(*params, nominatim_env=args)
175 Start a simple web server for serving the API.
177 This command starts the built-in PHP webserver to serve the website
178 from the current project directory. This webserver is only suitable
179 for testing and develop. Do not use it in production setups!
181 By the default, the webserver can be accessed at: http://127.0.0.1:8088
185 def add_args(parser):
186 group = parser.add_argument_group('Server arguments')
187 group.add_argument('--server', default='127.0.0.1:8088',
188 help='The address the server will listen to.')
192 run_php_server(args.server, args.project_dir / 'website')
194 def get_set_parser(**kwargs):
196 Initializes the parser and adds various subcommands for
199 parser = CommandlineParser('nominatim', nominatim.__doc__)
201 parser.add_subcommand('import', clicmd.SetupAll)
202 parser.add_subcommand('freeze', clicmd.SetupFreeze)
203 parser.add_subcommand('replication', clicmd.UpdateReplication)
205 parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases)
207 parser.add_subcommand('add-data', clicmd.UpdateAddData)
208 parser.add_subcommand('index', clicmd.UpdateIndex)
209 parser.add_subcommand('refresh', clicmd.UpdateRefresh())
211 parser.add_subcommand('admin', clicmd.AdminFuncs)
213 parser.add_subcommand('export', QueryExport)
214 parser.add_subcommand('serve', AdminServe)
216 if kwargs.get('phpcgi_path'):
217 parser.add_subcommand('search', clicmd.APISearch)
218 parser.add_subcommand('reverse', clicmd.APIReverse)
219 parser.add_subcommand('lookup', clicmd.APILookup)
220 parser.add_subcommand('details', clicmd.APIDetails)
221 parser.add_subcommand('status', clicmd.APIStatus)
223 parser.parser.epilog = 'php-cgi not found. Query commands not available.'
228 def nominatim(**kwargs):
230 Command-line tools for importing, updating, administrating and
231 querying the Nominatim database.
233 parser = get_set_parser(**kwargs)
235 return parser.run(**kwargs)