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]
81 # Arguments to 'import'
83 continue_at: Optional[str]
91 # Arguments to 'index'
97 # Arguments to 'export'
100 output_all_postcodes: bool
101 language: Optional[str]
102 restrict_to_country: Optional[str]
104 # Arguments to 'convert'
107 # Arguments to 'refresh'
114 secondary_importance: bool
118 enable_debug_statements: bool
119 data_object: Sequence[Tuple[str, int]]
120 data_area: Sequence[Tuple[str, int]]
122 # Arguments to 'replication'
124 update_functions: bool
125 check_for_updates: bool
130 # Arguments to 'serve'
134 # Arguments to 'special-phrases
135 import_from_wiki: bool
136 import_from_csv: Optional[str]
139 # Arguments to all query functions
145 polygon_output: Optional[str]
146 polygon_threshold: Optional[float]
148 # Arguments to 'search'
150 amenity: Optional[str]
151 street: Optional[str]
153 county: Optional[str]
155 country: Optional[str]
156 postalcode: Optional[str]
157 countrycodes: Optional[str]
158 exclude_place_ids: Optional[str]
160 viewbox: Optional[str]
164 # Arguments to 'reverse'
168 layers: Optional[Sequence[str]]
170 # Arguments to 'lookup'
173 # Arguments to 'details'
174 object_class: Optional[str]
178 polygon_geojson: bool
179 group_hierarchy: bool
182 def osm2pgsql_options(self, default_cache: int,
183 default_threads: int) -> Dict[str, Any]:
184 """ Return the standard osm2pgsql options that can be derived
185 from the command line arguments. The resulting dict can be
186 further customized and then used in `run_osm2pgsql()`.
188 return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
189 osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
190 osm2pgsql_style=self.config.get_import_style_file(),
191 osm2pgsql_style_path=self.config.config_dir,
192 threads=self.threads or default_threads,
193 dsn=self.config.get_libpq_dsn(),
194 flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
195 tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
196 slim_index=self.config.TABLESPACE_OSM_INDEX,
197 main_data=self.config.TABLESPACE_PLACE_DATA,
198 main_index=self.config.TABLESPACE_PLACE_INDEX
203 def get_osm_file_list(self) -> Optional[List[Path]]:
204 """ Return the --osm-file argument as a list of Paths or None
205 if no argument was given. The function also checks if the files
206 exist and raises a UsageError if one cannot be found.
208 if not self.osm_file:
211 files = [Path(f) for f in self.osm_file]
213 if not fname.is_file():
214 LOG.fatal("OSM file '%s' does not exist.", fname)
215 raise UsageError('Cannot access file.')
220 def get_geometry_output(self) -> napi.GeometryFormat:
221 """ Get the requested geometry output format in a API-compatible
224 if not self.polygon_output:
225 return napi.GeometryFormat.NONE
226 if self.polygon_output == 'geojson':
227 return napi.GeometryFormat.GEOJSON
228 if self.polygon_output == 'kml':
229 return napi.GeometryFormat.KML
230 if self.polygon_output == 'svg':
231 return napi.GeometryFormat.SVG
232 if self.polygon_output == 'text':
233 return napi.GeometryFormat.TEXT
236 return napi.GeometryFormat[self.polygon_output.upper()]
237 except KeyError as exp:
238 raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
241 def get_locales(self, default: Optional[str]) -> napi.Locales:
242 """ Get the locales from the language parameter.
245 return napi.Locales.from_accept_languages(self.lang)
247 return napi.Locales.from_accept_languages(default)
249 return napi.Locales()
252 def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
253 """ Get the list of selected layers as a DataLayer enum.
258 return reduce(napi.DataLayer.__or__,
259 (napi.DataLayer[s.upper()] for s in self.layers))