1 # SPDX-License-Identifier: GPL-2.0-only
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2022 by the Nominatim developer community.
6 # For a full list of authors see the git log.
8 Provides custom functions over command-line arguments.
10 from typing import Optional, List, Dict, Any, Sequence, Tuple
13 from functools import reduce
14 from pathlib import Path
16 from nominatim.errors import UsageError
17 from nominatim.config import Configuration
18 from nominatim.typing import Protocol
19 import nominatim.api as napi
21 LOG = logging.getLogger()
23 class Subcommand(Protocol):
25 Interface to be implemented by classes implementing a CLI subcommand.
28 def add_args(self, parser: argparse.ArgumentParser) -> None:
30 Fill the given parser for the subcommand with the appropriate
34 def run(self, args: 'NominatimArgs') -> int:
36 Run the subcommand with the given parsed arguments.
41 """ Customized namespace class for the nominatim command line tool
42 to receive the command-line arguments.
44 # Basic environment set by root program.
50 subcommand: Optional[str]
54 osm2pgsql_cache: Optional[int]
57 # Arguments added to all subcommands.
59 threads: Optional[int]
61 # Arguments to 'add-data'
66 relation: Optional[int]
67 tiger_data: Optional[str]
70 # Arguments to 'admin'
76 analyse_indexing: bool
79 place_id: Optional[int]
82 # Arguments to 'import'
84 continue_at: Optional[str]
92 # Arguments to 'index'
98 # Arguments to 'export'
101 output_all_postcodes: bool
102 language: Optional[str]
103 restrict_to_country: Optional[str]
105 # Arguments to 'refresh'
112 secondary_importance: bool
116 enable_debug_statements: bool
117 data_object: Sequence[Tuple[str, int]]
118 data_area: Sequence[Tuple[str, int]]
120 # Arguments to 'replication'
122 update_functions: bool
123 check_for_updates: bool
128 # Arguments to 'serve'
132 # Arguments to 'special-phrases
133 import_from_wiki: bool
134 import_from_csv: Optional[str]
137 # Arguments to all query functions
143 polygon_output: Optional[str]
144 polygon_threshold: Optional[float]
146 # Arguments to 'search'
148 amenity: Optional[str]
149 street: Optional[str]
151 county: Optional[str]
153 country: Optional[str]
154 postalcode: Optional[str]
155 countrycodes: Optional[str]
156 exclude_place_ids: Optional[str]
158 viewbox: Optional[str]
162 # Arguments to 'reverse'
166 layers: Optional[Sequence[str]]
168 # Arguments to 'lookup'
171 # Arguments to 'details'
172 object_class: Optional[str]
176 polygon_geojson: bool
177 group_hierarchy: bool
180 def osm2pgsql_options(self, default_cache: int,
181 default_threads: int) -> Dict[str, Any]:
182 """ Return the standard osm2pgsql options that can be derived
183 from the command line arguments. The resulting dict can be
184 further customized and then used in `run_osm2pgsql()`.
186 return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
187 osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
188 osm2pgsql_style=self.config.get_import_style_file(),
189 osm2pgsql_style_path=self.config.config_dir,
190 threads=self.threads or default_threads,
191 dsn=self.config.get_libpq_dsn(),
192 flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
193 tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
194 slim_index=self.config.TABLESPACE_OSM_INDEX,
195 main_data=self.config.TABLESPACE_PLACE_DATA,
196 main_index=self.config.TABLESPACE_PLACE_INDEX
201 def get_osm_file_list(self) -> Optional[List[Path]]:
202 """ Return the --osm-file argument as a list of Paths or None
203 if no argument was given. The function also checks if the files
204 exist and raises a UsageError if one cannot be found.
206 if not self.osm_file:
209 files = [Path(f) for f in self.osm_file]
211 if not fname.is_file():
212 LOG.fatal("OSM file '%s' does not exist.", fname)
213 raise UsageError('Cannot access file.')
218 def get_geometry_output(self) -> napi.GeometryFormat:
219 """ Get the requested geometry output format in a API-compatible
222 if not self.polygon_output:
223 return napi.GeometryFormat.NONE
224 if self.polygon_output == 'geojson':
225 return napi.GeometryFormat.GEOJSON
226 if self.polygon_output == 'kml':
227 return napi.GeometryFormat.KML
228 if self.polygon_output == 'svg':
229 return napi.GeometryFormat.SVG
230 if self.polygon_output == 'text':
231 return napi.GeometryFormat.TEXT
234 return napi.GeometryFormat[self.polygon_output.upper()]
235 except KeyError as exp:
236 raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
239 def get_locales(self, default: Optional[str]) -> napi.Locales:
240 """ Get the locales from the language parameter.
243 return napi.Locales.from_accept_languages(self.lang)
245 return napi.Locales.from_accept_languages(default)
247 return napi.Locales()
250 def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
251 """ Get the list of selected layers as a DataLayer enum.
256 return reduce(napi.DataLayer.__or__,
257 (napi.DataLayer[s.upper()] for s in self.layers))