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 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))