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 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.tiger_data,
152 args.config, args.threads or 1)
154 params = ['update.php']
156 params.extend(('--import-file', args.file))
158 params.extend(('--import-diff', args.diff))
160 params.extend(('--import-node', args.node))
162 params.extend(('--import-way', args.way))
164 params.extend(('--import-relation', args.relation))
165 if args.use_main_api:
166 params.append('--use-main-api')
167 return run_legacy_script(*params, nominatim_env=args)
172 Export addresses as CSV file from the database.
176 def add_args(parser):
177 group = parser.add_argument_group('Output arguments')
178 group.add_argument('--output-type', default='street',
179 choices=('continent', 'country', 'state', 'county',
180 'city', 'suburb', 'street', 'path'),
181 help='Type of places to output (default: street)')
182 group.add_argument('--output-format',
183 default='street;suburb;city;county;state;country',
184 help=("Semicolon-separated list of address types "
185 "(see --output-type). Multiple ranks can be "
186 "merged into one column by simply using a "
187 "comma-separated list."))
188 group.add_argument('--output-all-postcodes', action='store_true',
189 help=("List all postcodes for address instead of "
190 "just the most likely one"))
191 group.add_argument('--language',
192 help=("Preferred language for output "
193 "(use local name, if omitted)"))
194 group = parser.add_argument_group('Filter arguments')
195 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
196 help='Export only objects within country')
197 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
198 help='Export only children of this OSM node')
199 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
200 help='Export only children of this OSM way')
201 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
202 help='Export only children of this OSM relation')
207 params = ['export.php',
208 '--output-type', args.output_type,
209 '--output-format', args.output_format]
210 if args.output_all_postcodes:
211 params.append('--output-all-postcodes')
213 params.extend(('--language', args.language))
214 if args.restrict_to_country:
215 params.extend(('--restrict-to-country', args.restrict_to_country))
216 if args.restrict_to_osm_node:
217 params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
218 if args.restrict_to_osm_way:
219 params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
220 if args.restrict_to_osm_relation:
221 params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
223 return run_legacy_script(*params, nominatim_env=args)
228 Start a simple web server for serving the API.
230 This command starts the built-in PHP webserver to serve the website
231 from the current project directory. This webserver is only suitable
232 for testing and develop. Do not use it in production setups!
234 By the default, the webserver can be accessed at: http://127.0.0.1:8088
238 def add_args(parser):
239 group = parser.add_argument_group('Server arguments')
240 group.add_argument('--server', default='127.0.0.1:8088',
241 help='The address the server will listen to.')
245 run_php_server(args.server, args.project_dir / 'website')
247 def get_set_parser(**kwargs):
249 Initializes the parser and adds various subcommands for
252 parser = CommandlineParser('nominatim', nominatim.__doc__)
254 parser.add_subcommand('import', clicmd.SetupAll)
255 parser.add_subcommand('freeze', clicmd.SetupFreeze)
256 parser.add_subcommand('replication', clicmd.UpdateReplication)
258 parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases)
260 parser.add_subcommand('add-data', UpdateAddData)
261 parser.add_subcommand('index', clicmd.UpdateIndex)
262 parser.add_subcommand('refresh', clicmd.UpdateRefresh)
264 parser.add_subcommand('admin', clicmd.AdminFuncs)
266 parser.add_subcommand('export', QueryExport)
267 parser.add_subcommand('serve', AdminServe)
269 if kwargs.get('phpcgi_path'):
270 parser.add_subcommand('search', clicmd.APISearch)
271 parser.add_subcommand('reverse', clicmd.APIReverse)
272 parser.add_subcommand('lookup', clicmd.APILookup)
273 parser.add_subcommand('details', clicmd.APIDetails)
274 parser.add_subcommand('status', clicmd.APIStatus)
276 parser.parser.epilog = 'php-cgi not found. Query commands not available.'
281 def nominatim(**kwargs):
283 Command-line tools for importing, updating, administrating and
284 querying the Nominatim database.
286 parser = get_set_parser(**kwargs)
288 return parser.run(**kwargs)