1 # SPDX-License-Identifier: GPL-3.0-or-later
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2024 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_core.errors import UsageError
17 from nominatim_core.config import Configuration
18 from nominatim_core.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 prepare_database: bool
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 'convert'
108 # Arguments to 'refresh'
115 secondary_importance: bool
119 enable_debug_statements: bool
120 data_object: Sequence[Tuple[str, int]]
121 data_area: Sequence[Tuple[str, int]]
123 # Arguments to 'replication'
125 update_functions: bool
126 check_for_updates: bool
131 # Arguments to 'serve'
135 # Arguments to 'special-phrases
136 import_from_wiki: bool
137 import_from_csv: Optional[str]
140 # Arguments to all query functions
146 polygon_output: Optional[str]
147 polygon_threshold: Optional[float]
149 # Arguments to 'search'
151 amenity: Optional[str]
152 street: Optional[str]
154 county: Optional[str]
156 country: Optional[str]
157 postalcode: Optional[str]
158 countrycodes: Optional[str]
159 exclude_place_ids: Optional[str]
161 viewbox: Optional[str]
165 # Arguments to 'reverse'
169 layers: Optional[Sequence[str]]
171 # Arguments to 'lookup'
174 # Arguments to 'details'
175 object_class: Optional[str]
179 polygon_geojson: bool
180 group_hierarchy: bool
183 def osm2pgsql_options(self, default_cache: int,
184 default_threads: int) -> Dict[str, Any]:
185 """ Return the standard osm2pgsql options that can be derived
186 from the command line arguments. The resulting dict can be
187 further customized and then used in `run_osm2pgsql()`.
189 return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
190 osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
191 osm2pgsql_style=self.config.get_import_style_file(),
192 osm2pgsql_style_path=self.config.config_dir,
193 threads=self.threads or default_threads,
194 dsn=self.config.get_libpq_dsn(),
195 flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
196 tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
197 slim_index=self.config.TABLESPACE_OSM_INDEX,
198 main_data=self.config.TABLESPACE_PLACE_DATA,
199 main_index=self.config.TABLESPACE_PLACE_INDEX
204 def get_osm_file_list(self) -> Optional[List[Path]]:
205 """ Return the --osm-file argument as a list of Paths or None
206 if no argument was given. The function also checks if the files
207 exist and raises a UsageError if one cannot be found.
209 if not self.osm_file:
212 files = [Path(f) for f in self.osm_file]
214 if not fname.is_file():
215 LOG.fatal("OSM file '%s' does not exist.", fname)
216 raise UsageError('Cannot access file.')
221 def get_geometry_output(self) -> napi.GeometryFormat:
222 """ Get the requested geometry output format in a API-compatible
225 if not self.polygon_output:
226 return napi.GeometryFormat.NONE
227 if self.polygon_output == 'geojson':
228 return napi.GeometryFormat.GEOJSON
229 if self.polygon_output == 'kml':
230 return napi.GeometryFormat.KML
231 if self.polygon_output == 'svg':
232 return napi.GeometryFormat.SVG
233 if self.polygon_output == 'text':
234 return napi.GeometryFormat.TEXT
237 return napi.GeometryFormat[self.polygon_output.upper()]
238 except KeyError as exp:
239 raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
242 def get_locales(self, default: Optional[str]) -> napi.Locales:
243 """ Get the locales from the language parameter.
246 return napi.Locales.from_accept_languages(self.lang)
248 return napi.Locales.from_accept_languages(default)
250 return napi.Locales()
253 def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
254 """ Get the list of selected layers as a DataLayer enum.
259 return reduce(napi.DataLayer.__or__,
260 (napi.DataLayer[s.upper()] for s in self.layers))