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 'convert'
 
 107     # Arguments to 'refresh'
 
 114     secondary_importance: bool
 
 118     enable_debug_statements: bool
 
 119     data_object: Sequence[Tuple[str, int]]
 
 120     data_area: Sequence[Tuple[str, int]]
 
 122     # Arguments to 'replication'
 
 124     update_functions: bool
 
 125     check_for_updates: bool
 
 130     # Arguments to 'serve'
 
 134     # Arguments to 'special-phrases
 
 135     import_from_wiki: bool
 
 136     import_from_csv: Optional[str]
 
 139     # Arguments to all query functions
 
 145     polygon_output: Optional[str]
 
 146     polygon_threshold: Optional[float]
 
 148     # Arguments to 'search'
 
 150     amenity: Optional[str]
 
 151     street: Optional[str]
 
 153     county: Optional[str]
 
 155     country: Optional[str]
 
 156     postalcode: Optional[str]
 
 157     countrycodes: Optional[str]
 
 158     exclude_place_ids: Optional[str]
 
 160     viewbox: Optional[str]
 
 164     # Arguments to 'reverse'
 
 168     layers: Optional[Sequence[str]]
 
 170     # Arguments to 'lookup'
 
 173     # Arguments to 'details'
 
 174     object_class: Optional[str]
 
 178     polygon_geojson: bool
 
 179     group_hierarchy: bool
 
 182     def osm2pgsql_options(self, default_cache: int,
 
 183                           default_threads: int) -> Dict[str, Any]:
 
 184         """ Return the standard osm2pgsql options that can be derived
 
 185             from the command line arguments. The resulting dict can be
 
 186             further customized and then used in `run_osm2pgsql()`.
 
 188         return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY or self.config.lib_dir.osm2pgsql,
 
 189                     osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
 
 190                     osm2pgsql_style=self.config.get_import_style_file(),
 
 191                     osm2pgsql_style_path=self.config.config_dir,
 
 192                     threads=self.threads or default_threads,
 
 193                     dsn=self.config.get_libpq_dsn(),
 
 194                     flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
 
 195                     tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
 
 196                                      slim_index=self.config.TABLESPACE_OSM_INDEX,
 
 197                                      main_data=self.config.TABLESPACE_PLACE_DATA,
 
 198                                      main_index=self.config.TABLESPACE_PLACE_INDEX
 
 203     def get_osm_file_list(self) -> Optional[List[Path]]:
 
 204         """ Return the --osm-file argument as a list of Paths or None
 
 205             if no argument was given. The function also checks if the files
 
 206             exist and raises a UsageError if one cannot be found.
 
 208         if not self.osm_file:
 
 211         files = [Path(f) for f in self.osm_file]
 
 213             if not fname.is_file():
 
 214                 LOG.fatal("OSM file '%s' does not exist.", fname)
 
 215                 raise UsageError('Cannot access file.')
 
 220     def get_geometry_output(self) -> napi.GeometryFormat:
 
 221         """ Get the requested geometry output format in a API-compatible
 
 224         if not self.polygon_output:
 
 225             return napi.GeometryFormat.NONE
 
 226         if self.polygon_output == 'geojson':
 
 227             return napi.GeometryFormat.GEOJSON
 
 228         if self.polygon_output == 'kml':
 
 229             return napi.GeometryFormat.KML
 
 230         if self.polygon_output == 'svg':
 
 231             return napi.GeometryFormat.SVG
 
 232         if self.polygon_output == 'text':
 
 233             return napi.GeometryFormat.TEXT
 
 236             return napi.GeometryFormat[self.polygon_output.upper()]
 
 237         except KeyError as exp:
 
 238             raise UsageError(f"Unknown polygon output format '{self.polygon_output}'.") from exp
 
 241     def get_locales(self, default: Optional[str]) -> napi.Locales:
 
 242         """ Get the locales from the language parameter.
 
 245             return napi.Locales.from_accept_languages(self.lang)
 
 247             return napi.Locales.from_accept_languages(default)
 
 249         return napi.Locales()
 
 252     def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
 
 253         """ Get the list of selected layers as a DataLayer enum.
 
 258         return reduce(napi.DataLayer.__or__,
 
 259                       (napi.DataLayer[s.upper()] for s in self.layers))