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'
75 analyse_indexing: bool
78 place_id: Optional[int]
80 # Arguments to 'import'
82 continue_at: Optional[str]
90 # Arguments to 'index'
96 # Arguments to 'export'
99 output_all_postcodes: bool
100 language: Optional[str]
101 restrict_to_country: Optional[str]
103 # Arguments to 'refresh'
110 secondary_importance: bool
114 enable_debug_statements: bool
115 data_object: Sequence[Tuple[str, int]]
116 data_area: Sequence[Tuple[str, int]]
118 # Arguments to 'replication'
120 update_functions: bool
121 check_for_updates: bool
126 # Arguments to 'serve'
130 # Arguments to 'special-phrases
131 import_from_wiki: bool
132 import_from_csv: Optional[str]
135 # Arguments to all query functions
141 polygon_output: Optional[str]
142 polygon_threshold: Optional[float]
144 # Arguments to 'search'
146 amenity: Optional[str]
147 street: Optional[str]
149 county: Optional[str]
151 country: Optional[str]
152 postalcode: Optional[str]
153 countrycodes: Optional[str]
154 exclude_place_ids: Optional[str]
156 viewbox: Optional[str]
160 # Arguments to 'reverse'
164 layers: Optional[Sequence[str]]
166 # Arguments to 'lookup'
169 # Arguments to 'details'
170 object_class: Optional[str]
174 polygon_geojson: bool
175 group_hierarchy: bool
178 def osm2pgsql_options(self, default_cache: int,
179 default_threads: int) -> Dict[str, Any]:
180 """ Return the standard osm2pgsql options that can be derived
181 from the command line arguments. The resulting dict can be
182 further customized and then used in `run_osm2pgsql()`.
184 return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
185 osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
186 osm2pgsql_style=self.config.get_import_style_file(),
187 osm2pgsql_style_path=self.config.config_dir,
188 threads=self.threads or default_threads,
189 dsn=self.config.get_libpq_dsn(),
190 flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
191 tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
192 slim_index=self.config.TABLESPACE_OSM_INDEX,
193 main_data=self.config.TABLESPACE_PLACE_DATA,
194 main_index=self.config.TABLESPACE_PLACE_INDEX
199 def get_osm_file_list(self) -> Optional[List[Path]]:
200 """ Return the --osm-file argument as a list of Paths or None
201 if no argument was given. The function also checks if the files
202 exist and raises a UsageError if one cannot be found.
204 if not self.osm_file:
207 files = [Path(f) for f in self.osm_file]
209 if not fname.is_file():
210 LOG.fatal("OSM file '%s' does not exist.", fname)
211 raise UsageError('Cannot access file.')
216 def get_geometry_output(self) -> napi.GeometryFormat:
217 """ Get the requested geometry output format in a API-compatible
220 if not self.polygon_output:
221 return napi.GeometryFormat.NONE
222 if self.polygon_output == 'geojson':
223 return napi.GeometryFormat.GEOJSON
224 if self.polygon_output == 'kml':
225 return napi.GeometryFormat.KML
226 if self.polygon_output == 'svg':
227 return napi.GeometryFormat.SVG
228 if self.polygon_output == 'text':
229 return napi.GeometryFormat.TEXT
232 return napi.GeometryFormat[self.polygon_output.upper()]
233 except KeyError as exp:
234 raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
237 def get_locales(self, default: Optional[str]) -> napi.Locales:
238 """ Get the locales from the language parameter.
241 return napi.Locales.from_accept_languages(self.lang)
243 return napi.Locales.from_accept_languages(default)
245 return napi.Locales()
248 def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
249 """ Get the list of selected layers as a DataLayer enum.
254 return reduce(napi.DataLayer.__or__,
255 (napi.DataLayer[s.upper()] for s in self.layers))