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]
90 only_import_data: bool
91 only_prepare_database: bool
93 # Arguments to 'index'
99 # Arguments to 'export'
102 output_all_postcodes: bool
103 language: Optional[str]
104 restrict_to_country: Optional[str]
106 # Arguments to 'convert'
109 # Arguments to 'refresh'
116 secondary_importance: bool
120 enable_debug_statements: bool
121 data_object: Sequence[Tuple[str, int]]
122 data_area: Sequence[Tuple[str, int]]
124 # Arguments to 'replication'
126 update_functions: bool
127 check_for_updates: bool
132 # Arguments to 'serve'
136 # Arguments to 'special-phrases
137 import_from_wiki: bool
138 import_from_csv: Optional[str]
141 # Arguments to all query functions
147 polygon_output: Optional[str]
148 polygon_threshold: Optional[float]
150 # Arguments to 'search'
152 amenity: Optional[str]
153 street: Optional[str]
155 county: Optional[str]
157 country: Optional[str]
158 postalcode: Optional[str]
159 countrycodes: Optional[str]
160 exclude_place_ids: Optional[str]
162 viewbox: Optional[str]
166 # Arguments to 'reverse'
170 layers: Optional[Sequence[str]]
172 # Arguments to 'lookup'
175 # Arguments to 'details'
176 object_class: Optional[str]
180 polygon_geojson: bool
181 group_hierarchy: bool
184 def osm2pgsql_options(self, default_cache: int,
185 default_threads: int) -> Dict[str, Any]:
186 """ Return the standard osm2pgsql options that can be derived
187 from the command line arguments. The resulting dict can be
188 further customized and then used in `run_osm2pgsql()`.
190 return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
191 osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
192 osm2pgsql_style=self.config.get_import_style_file(),
193 osm2pgsql_style_path=self.config.config_dir,
194 threads=self.threads or default_threads,
195 dsn=self.config.get_libpq_dsn(),
196 flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
197 tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
198 slim_index=self.config.TABLESPACE_OSM_INDEX,
199 main_data=self.config.TABLESPACE_PLACE_DATA,
200 main_index=self.config.TABLESPACE_PLACE_INDEX
205 def get_osm_file_list(self) -> Optional[List[Path]]:
206 """ Return the --osm-file argument as a list of Paths or None
207 if no argument was given. The function also checks if the files
208 exist and raises a UsageError if one cannot be found.
210 if not self.osm_file:
213 files = [Path(f) for f in self.osm_file]
215 if not fname.is_file():
216 LOG.fatal("OSM file '%s' does not exist.", fname)
217 raise UsageError('Cannot access file.')
222 def get_geometry_output(self) -> napi.GeometryFormat:
223 """ Get the requested geometry output format in a API-compatible
226 if not self.polygon_output:
227 return napi.GeometryFormat.NONE
228 if self.polygon_output == 'geojson':
229 return napi.GeometryFormat.GEOJSON
230 if self.polygon_output == 'kml':
231 return napi.GeometryFormat.KML
232 if self.polygon_output == 'svg':
233 return napi.GeometryFormat.SVG
234 if self.polygon_output == 'text':
235 return napi.GeometryFormat.TEXT
238 return napi.GeometryFormat[self.polygon_output.upper()]
239 except KeyError as exp:
240 raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
243 def get_locales(self, default: Optional[str]) -> napi.Locales:
244 """ Get the locales from the language parameter.
247 return napi.Locales.from_accept_languages(self.lang)
249 return napi.Locales.from_accept_languages(default)
251 return napi.Locales()
254 def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
255 """ Get the list of selected layers as a DataLayer enum.
260 return reduce(napi.DataLayer.__or__,
261 (napi.DataLayer[s.upper()] for s in self.layers))