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 'refresh'
111 secondary_importance: bool
115 enable_debug_statements: bool
116 data_object: Sequence[Tuple[str, int]]
117 data_area: Sequence[Tuple[str, int]]
119 # Arguments to 'replication'
121 update_functions: bool
122 check_for_updates: bool
127 # Arguments to 'serve'
131 # Arguments to 'special-phrases
132 import_from_wiki: bool
133 import_from_csv: Optional[str]
136 # Arguments to all query functions
142 polygon_output: Optional[str]
143 polygon_threshold: Optional[float]
145 # Arguments to 'search'
147 amenity: Optional[str]
148 street: Optional[str]
150 county: Optional[str]
152 country: Optional[str]
153 postalcode: Optional[str]
154 countrycodes: Optional[str]
155 exclude_place_ids: Optional[str]
157 viewbox: Optional[str]
161 # Arguments to 'reverse'
165 layers: Optional[Sequence[str]]
167 # Arguments to 'lookup'
170 # Arguments to 'details'
171 object_class: Optional[str]
175 polygon_geojson: bool
176 group_hierarchy: bool
179 def osm2pgsql_options(self, default_cache: int,
180 default_threads: int) -> Dict[str, Any]:
181 """ Return the standard osm2pgsql options that can be derived
182 from the command line arguments. The resulting dict can be
183 further customized and then used in `run_osm2pgsql()`.
185 return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
186 osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
187 osm2pgsql_style=self.config.get_import_style_file(),
188 osm2pgsql_style_path=self.config.config_dir,
189 threads=self.threads or default_threads,
190 dsn=self.config.get_libpq_dsn(),
191 flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
192 tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
193 slim_index=self.config.TABLESPACE_OSM_INDEX,
194 main_data=self.config.TABLESPACE_PLACE_DATA,
195 main_index=self.config.TABLESPACE_PLACE_INDEX
200 def get_osm_file_list(self) -> Optional[List[Path]]:
201 """ Return the --osm-file argument as a list of Paths or None
202 if no argument was given. The function also checks if the files
203 exist and raises a UsageError if one cannot be found.
205 if not self.osm_file:
208 files = [Path(f) for f in self.osm_file]
210 if not fname.is_file():
211 LOG.fatal("OSM file '%s' does not exist.", fname)
212 raise UsageError('Cannot access file.')
217 def get_geometry_output(self) -> napi.GeometryFormat:
218 """ Get the requested geometry output format in a API-compatible
221 if not self.polygon_output:
222 return napi.GeometryFormat.NONE
223 if self.polygon_output == 'geojson':
224 return napi.GeometryFormat.GEOJSON
225 if self.polygon_output == 'kml':
226 return napi.GeometryFormat.KML
227 if self.polygon_output == 'svg':
228 return napi.GeometryFormat.SVG
229 if self.polygon_output == 'text':
230 return napi.GeometryFormat.TEXT
233 return napi.GeometryFormat[self.polygon_output.upper()]
234 except KeyError as exp:
235 raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
238 def get_locales(self, default: Optional[str]) -> napi.Locales:
239 """ Get the locales from the language parameter.
242 return napi.Locales.from_accept_languages(self.lang)
244 return napi.Locales.from_accept_languages(default)
246 return napi.Locales()
249 def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
250 """ Get the list of selected layers as a DataLayer enum.
255 return reduce(napi.DataLayer.__or__,
256 (napi.DataLayer[s.upper()] for s in self.layers))