2022-01-03 18:23:58 +03:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#
|
|
|
|
# This file is part of Nominatim. (https://nominatim.org)
|
|
|
|
#
|
|
|
|
# Copyright (C) 2022 by the Nominatim developer community.
|
|
|
|
# For a full list of authors see the git log.
|
2021-01-12 12:17:28 +03:00
|
|
|
"""
|
2021-01-13 00:21:20 +03:00
|
|
|
Command-line interface to the Nominatim functions for import, update,
|
|
|
|
database administration and querying.
|
2021-01-12 12:17:28 +03:00
|
|
|
"""
|
2021-02-08 19:23:05 +03:00
|
|
|
import logging
|
2021-01-14 14:03:49 +03:00
|
|
|
import os
|
2021-01-30 17:50:34 +03:00
|
|
|
import sys
|
2021-01-13 00:21:20 +03:00
|
|
|
import argparse
|
2021-01-13 20:25:15 +03:00
|
|
|
from pathlib import Path
|
|
|
|
|
2021-04-16 15:20:09 +03:00
|
|
|
from nominatim.config import Configuration
|
|
|
|
from nominatim.tools.exec_utils import run_legacy_script, run_php_server
|
|
|
|
from nominatim.errors import UsageError
|
|
|
|
from nominatim import clicmd
|
2022-05-04 02:33:25 +03:00
|
|
|
from nominatim import version
|
2021-04-16 15:20:09 +03:00
|
|
|
from nominatim.clicmd.args import NominatimArgs
|
2021-01-13 00:21:20 +03:00
|
|
|
|
2021-01-23 01:25:37 +03:00
|
|
|
LOG = logging.getLogger()
|
|
|
|
|
2021-01-17 22:05:41 +03:00
|
|
|
|
2021-01-13 00:21:20 +03:00
|
|
|
class CommandlineParser:
|
|
|
|
""" Wraps some of the common functions for parsing the command line
|
|
|
|
and setting up subcommands.
|
|
|
|
"""
|
|
|
|
def __init__(self, prog, description):
|
|
|
|
self.parser = argparse.ArgumentParser(
|
|
|
|
prog=prog,
|
|
|
|
description=description,
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
|
|
|
|
self.subs = self.parser.add_subparsers(title='available commands',
|
|
|
|
dest='subcommand')
|
|
|
|
|
2022-05-04 02:33:25 +03:00
|
|
|
# Global arguments that only work if no sub-command given
|
2022-05-04 16:48:23 +03:00
|
|
|
self.parser.add_argument('--version', action='store_true',
|
2022-05-04 02:33:25 +03:00
|
|
|
help='Print Nominatim version and exit')
|
|
|
|
|
2021-01-13 00:21:20 +03:00
|
|
|
# Arguments added to every sub-command
|
|
|
|
self.default_args = argparse.ArgumentParser(add_help=False)
|
|
|
|
group = self.default_args.add_argument_group('Default arguments')
|
|
|
|
group.add_argument('-h', '--help', action='help',
|
|
|
|
help='Show this help message and exit')
|
|
|
|
group.add_argument('-q', '--quiet', action='store_const', const=0,
|
|
|
|
dest='verbose', default=1,
|
|
|
|
help='Print only error messages')
|
|
|
|
group.add_argument('-v', '--verbose', action='count', default=1,
|
|
|
|
help='Increase verboseness of output')
|
2021-01-13 20:25:15 +03:00
|
|
|
group.add_argument('--project-dir', metavar='DIR', default='.',
|
2021-01-13 00:21:20 +03:00
|
|
|
help='Base directory of the Nominatim installation (default:.)')
|
|
|
|
group.add_argument('-j', '--threads', metavar='NUM', type=int,
|
|
|
|
help='Number of parallel threads to use')
|
|
|
|
|
2022-05-04 02:33:25 +03:00
|
|
|
@staticmethod
|
|
|
|
def nominatim_version_text():
|
|
|
|
""" Program name and version number as string
|
|
|
|
"""
|
2022-05-11 00:00:18 +03:00
|
|
|
text = f'Nominatim version {version.version_str()}'
|
2022-05-04 16:48:23 +03:00
|
|
|
if version.GIT_COMMIT_HASH is not None:
|
2022-05-11 00:00:18 +03:00
|
|
|
text += f' ({version.GIT_COMMIT_HASH})'
|
2022-05-04 16:48:23 +03:00
|
|
|
return text
|
2021-01-13 00:21:20 +03:00
|
|
|
|
|
|
|
def add_subcommand(self, name, cmd):
|
|
|
|
""" Add a subcommand to the parser. The subcommand must be a class
|
|
|
|
with a function add_args() that adds the parameters for the
|
|
|
|
subcommand and a run() function that executes the command.
|
|
|
|
"""
|
|
|
|
parser = self.subs.add_parser(name, parents=[self.default_args],
|
|
|
|
help=cmd.__doc__.split('\n', 1)[0],
|
|
|
|
description=cmd.__doc__,
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
|
|
add_help=False)
|
|
|
|
parser.set_defaults(command=cmd)
|
|
|
|
cmd.add_args(parser)
|
|
|
|
|
2021-01-13 20:25:15 +03:00
|
|
|
def run(self, **kwargs):
|
2021-01-13 00:21:20 +03:00
|
|
|
""" Parse the command line arguments of the program and execute the
|
|
|
|
appropriate subcommand.
|
|
|
|
"""
|
2021-02-24 12:38:19 +03:00
|
|
|
args = NominatimArgs()
|
2022-04-14 15:52:13 +03:00
|
|
|
try:
|
|
|
|
self.parser.parse_args(args=kwargs.get('cli_args'), namespace=args)
|
|
|
|
except SystemExit:
|
|
|
|
return 1
|
2021-01-13 00:21:20 +03:00
|
|
|
|
2022-05-04 16:48:23 +03:00
|
|
|
if args.version:
|
|
|
|
print(CommandlineParser.nominatim_version_text())
|
|
|
|
return 0
|
|
|
|
|
2021-01-13 00:21:20 +03:00
|
|
|
if args.subcommand is None:
|
2021-01-20 11:04:04 +03:00
|
|
|
self.parser.print_help()
|
|
|
|
return 1
|
2021-01-13 20:25:15 +03:00
|
|
|
|
2021-02-09 17:26:56 +03:00
|
|
|
for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'sqllib_dir',
|
2021-02-09 20:45:45 +03:00
|
|
|
'data_dir', 'config_dir', 'phpcgi_path'):
|
2021-01-13 20:25:15 +03:00
|
|
|
setattr(args, arg, Path(kwargs[arg]))
|
2021-02-02 19:33:41 +03:00
|
|
|
args.project_dir = Path(args.project_dir).resolve()
|
2021-01-13 20:25:15 +03:00
|
|
|
|
2021-02-26 18:14:29 +03:00
|
|
|
if 'cli_args' not in kwargs:
|
|
|
|
logging.basicConfig(stream=sys.stderr,
|
|
|
|
format='%(asctime)s: %(message)s',
|
|
|
|
datefmt='%Y-%m-%d %H:%M:%S',
|
|
|
|
level=max(4 - args.verbose, 1) * 10)
|
|
|
|
|
|
|
|
args.config = Configuration(args.project_dir, args.config_dir,
|
|
|
|
environ=kwargs.get('environ', os.environ))
|
2021-04-19 10:06:42 +03:00
|
|
|
args.config.set_libdirs(module=args.module_dir,
|
|
|
|
osm2pgsql=args.osm2pgsql_path,
|
|
|
|
php=args.phplib_dir,
|
|
|
|
sql=args.sqllib_dir,
|
|
|
|
data=args.data_dir)
|
2021-01-13 20:25:15 +03:00
|
|
|
|
2021-02-02 13:19:31 +03:00
|
|
|
log = logging.getLogger()
|
2021-02-02 19:33:41 +03:00
|
|
|
log.warning('Using project directory: %s', str(args.project_dir))
|
2021-02-02 13:19:31 +03:00
|
|
|
|
2021-01-30 18:20:10 +03:00
|
|
|
try:
|
|
|
|
return args.command.run(args)
|
2021-01-30 18:42:25 +03:00
|
|
|
except UsageError as exception:
|
2021-01-30 18:20:10 +03:00
|
|
|
if log.isEnabledFor(logging.DEBUG):
|
|
|
|
raise # use Python's exception printing
|
2021-01-30 18:42:25 +03:00
|
|
|
log.fatal('FATAL: %s', exception)
|
2021-01-30 18:20:10 +03:00
|
|
|
|
|
|
|
# If we get here, then execution has failed in some way.
|
|
|
|
return 1
|
2021-01-14 12:19:21 +03:00
|
|
|
|
2021-01-30 17:50:34 +03:00
|
|
|
|
2021-07-12 18:45:42 +03:00
|
|
|
# Subcommand classes
|
2021-01-14 12:19:21 +03:00
|
|
|
#
|
|
|
|
# Each class needs to implement two functions: add_args() adds the CLI parameters
|
|
|
|
# for the subfunction, run() executes the subcommand.
|
|
|
|
#
|
|
|
|
# The class documentation doubles as the help text for the command. The
|
|
|
|
# first line is also used in the summary when calling the program without
|
|
|
|
# a subcommand.
|
|
|
|
#
|
|
|
|
# No need to document the functions each time.
|
|
|
|
# pylint: disable=C0111
|
2021-01-13 12:11:58 +03:00
|
|
|
class QueryExport:
|
2021-01-13 00:21:20 +03:00
|
|
|
"""\
|
2021-01-18 00:03:54 +03:00
|
|
|
Export addresses as CSV file from the database.
|
2021-01-13 00:21:20 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def add_args(parser):
|
|
|
|
group = parser.add_argument_group('Output arguments')
|
|
|
|
group.add_argument('--output-type', default='street',
|
|
|
|
choices=('continent', 'country', 'state', 'county',
|
|
|
|
'city', 'suburb', 'street', 'path'),
|
|
|
|
help='Type of places to output (default: street)')
|
|
|
|
group.add_argument('--output-format',
|
|
|
|
default='street;suburb;city;county;state;country',
|
2021-03-29 11:30:45 +03:00
|
|
|
help=("Semicolon-separated list of address types "
|
|
|
|
"(see --output-type). Multiple ranks can be "
|
|
|
|
"merged into one column by simply using a "
|
|
|
|
"comma-separated list."))
|
2021-01-13 00:21:20 +03:00
|
|
|
group.add_argument('--output-all-postcodes', action='store_true',
|
2021-03-29 11:30:45 +03:00
|
|
|
help=("List all postcodes for address instead of "
|
|
|
|
"just the most likely one"))
|
2021-01-13 00:21:20 +03:00
|
|
|
group.add_argument('--language',
|
2021-03-29 11:30:45 +03:00
|
|
|
help=("Preferred language for output "
|
|
|
|
"(use local name, if omitted)"))
|
2021-01-13 00:21:20 +03:00
|
|
|
group = parser.add_argument_group('Filter arguments')
|
|
|
|
group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
|
|
|
|
help='Export only objects within country')
|
|
|
|
group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
|
|
|
|
help='Export only children of this OSM node')
|
|
|
|
group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
|
|
|
|
help='Export only children of this OSM way')
|
|
|
|
group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
|
|
|
|
help='Export only children of this OSM relation')
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def run(args):
|
2021-01-14 11:37:47 +03:00
|
|
|
params = ['export.php',
|
|
|
|
'--output-type', args.output_type,
|
|
|
|
'--output-format', args.output_format]
|
|
|
|
if args.output_all_postcodes:
|
|
|
|
params.append('--output-all-postcodes')
|
|
|
|
if args.language:
|
|
|
|
params.extend(('--language', args.language))
|
|
|
|
if args.restrict_to_country:
|
|
|
|
params.extend(('--restrict-to-country', args.restrict_to_country))
|
|
|
|
if args.restrict_to_osm_node:
|
2021-01-14 12:19:21 +03:00
|
|
|
params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
|
2021-01-14 11:37:47 +03:00
|
|
|
if args.restrict_to_osm_way:
|
2021-01-14 12:19:21 +03:00
|
|
|
params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
|
2021-01-14 11:37:47 +03:00
|
|
|
if args.restrict_to_osm_relation:
|
2021-01-14 12:19:21 +03:00
|
|
|
params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
|
2021-01-12 12:17:28 +03:00
|
|
|
|
2021-01-14 11:37:47 +03:00
|
|
|
return run_legacy_script(*params, nominatim_env=args)
|
2021-01-12 12:17:28 +03:00
|
|
|
|
2021-02-03 18:17:46 +03:00
|
|
|
|
|
|
|
class AdminServe:
|
|
|
|
"""\
|
|
|
|
Start a simple web server for serving the API.
|
|
|
|
|
|
|
|
This command starts the built-in PHP webserver to serve the website
|
|
|
|
from the current project directory. This webserver is only suitable
|
2021-10-12 00:27:38 +03:00
|
|
|
for testing and development. Do not use it in production setups!
|
2021-02-03 18:17:46 +03:00
|
|
|
|
|
|
|
By the default, the webserver can be accessed at: http://127.0.0.1:8088
|
|
|
|
"""
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def add_args(parser):
|
|
|
|
group = parser.add_argument_group('Server arguments')
|
|
|
|
group.add_argument('--server', default='127.0.0.1:8088',
|
|
|
|
help='The address the server will listen to.')
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def run(args):
|
|
|
|
run_php_server(args.server, args.project_dir / 'website')
|
|
|
|
|
2021-03-23 05:40:53 +03:00
|
|
|
def get_set_parser(**kwargs):
|
2021-01-13 00:21:20 +03:00
|
|
|
"""\
|
2021-03-23 05:40:53 +03:00
|
|
|
Initializes the parser and adds various subcommands for
|
|
|
|
nominatim cli.
|
2021-01-13 00:21:20 +03:00
|
|
|
"""
|
2021-01-13 12:11:58 +03:00
|
|
|
parser = CommandlineParser('nominatim', nominatim.__doc__)
|
2021-01-13 00:21:20 +03:00
|
|
|
|
2021-02-26 17:02:39 +03:00
|
|
|
parser.add_subcommand('import', clicmd.SetupAll)
|
2021-02-17 23:43:15 +03:00
|
|
|
parser.add_subcommand('freeze', clicmd.SetupFreeze)
|
2021-02-08 19:23:05 +03:00
|
|
|
parser.add_subcommand('replication', clicmd.UpdateReplication)
|
2021-01-13 00:21:20 +03:00
|
|
|
|
2021-03-26 03:57:51 +03:00
|
|
|
parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases)
|
2021-01-13 00:21:20 +03:00
|
|
|
|
2021-07-25 19:14:12 +03:00
|
|
|
parser.add_subcommand('add-data', clicmd.UpdateAddData)
|
2021-02-08 19:23:05 +03:00
|
|
|
parser.add_subcommand('index', clicmd.UpdateIndex)
|
2021-05-25 20:16:22 +03:00
|
|
|
parser.add_subcommand('refresh', clicmd.UpdateRefresh())
|
2021-01-13 12:11:58 +03:00
|
|
|
|
2021-02-08 23:05:06 +03:00
|
|
|
parser.add_subcommand('admin', clicmd.AdminFuncs)
|
|
|
|
|
2021-01-13 12:11:58 +03:00
|
|
|
parser.add_subcommand('export', QueryExport)
|
2021-02-03 18:17:46 +03:00
|
|
|
parser.add_subcommand('serve', AdminServe)
|
2021-01-18 00:03:54 +03:00
|
|
|
|
|
|
|
if kwargs.get('phpcgi_path'):
|
2021-02-08 19:23:05 +03:00
|
|
|
parser.add_subcommand('search', clicmd.APISearch)
|
|
|
|
parser.add_subcommand('reverse', clicmd.APIReverse)
|
|
|
|
parser.add_subcommand('lookup', clicmd.APILookup)
|
|
|
|
parser.add_subcommand('details', clicmd.APIDetails)
|
|
|
|
parser.add_subcommand('status', clicmd.APIStatus)
|
2021-01-18 00:03:54 +03:00
|
|
|
else:
|
|
|
|
parser.parser.epilog = 'php-cgi not found. Query commands not available.'
|
2021-01-12 12:17:28 +03:00
|
|
|
|
2021-03-23 05:40:53 +03:00
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
|
|
def nominatim(**kwargs):
|
|
|
|
"""\
|
|
|
|
Command-line tools for importing, updating, administrating and
|
|
|
|
querying the Nominatim database.
|
|
|
|
"""
|
|
|
|
parser = get_set_parser(**kwargs)
|
|
|
|
|
2021-01-14 12:19:21 +03:00
|
|
|
return parser.run(**kwargs)
|