]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/clicmd/args.py
switch CLI search command to python implementation
[nominatim.git] / nominatim / clicmd / args.py
1 # SPDX-License-Identifier: GPL-2.0-only
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2022 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Provides custom functions over command-line arguments.
9 """
10 from typing import Optional, List, Dict, Any, Sequence, Tuple
11 import argparse
12 import logging
13 from functools import reduce
14 from pathlib import Path
15
16 from nominatim.errors import UsageError
17 from nominatim.config import Configuration
18 from nominatim.typing import Protocol
19 import nominatim.api as napi
20
21 LOG = logging.getLogger()
22
23 class Subcommand(Protocol):
24     """
25     Interface to be implemented by classes implementing a CLI subcommand.
26     """
27
28     def add_args(self, parser: argparse.ArgumentParser) -> None:
29         """
30         Fill the given parser for the subcommand with the appropriate
31         parameters.
32         """
33
34     def run(self, args: 'NominatimArgs') -> int:
35         """
36         Run the subcommand with the given parsed arguments.
37         """
38
39
40 class NominatimArgs:
41     """ Customized namespace class for the nominatim command line tool
42         to receive the command-line arguments.
43     """
44     # Basic environment set by root program.
45     config: Configuration
46     project_dir: Path
47     phpcgi_path: Path
48
49     # Global switches
50     version: bool
51     subcommand: Optional[str]
52     command: Subcommand
53
54     # Shared parameters
55     osm2pgsql_cache: Optional[int]
56     socket_timeout: int
57
58     # Arguments added to all subcommands.
59     verbose: int
60     threads: Optional[int]
61
62     # Arguments to 'add-data'
63     file: Optional[str]
64     diff: Optional[str]
65     node: Optional[int]
66     way: Optional[int]
67     relation: Optional[int]
68     tiger_data: Optional[str]
69     use_main_api: bool
70
71     # Arguments to 'admin'
72     warm: bool
73     check_database: bool
74     migrate: bool
75     collect_os_info: bool
76     analyse_indexing: bool
77     target: Optional[str]
78     osm_id: Optional[str]
79     place_id: Optional[int]
80
81     # Arguments to 'import'
82     osm_file: List[str]
83     continue_at: Optional[str]
84     reverse_only: bool
85     no_partitions: bool
86     no_updates: bool
87     offline: bool
88     ignore_errors: bool
89     index_noanalyse: bool
90
91     # Arguments to 'index'
92     boundaries_only: bool
93     no_boundaries: bool
94     minrank: int
95     maxrank: int
96
97     # Arguments to 'export'
98     output_type: str
99     output_format: str
100     output_all_postcodes: bool
101     language: Optional[str]
102     restrict_to_country: Optional[str]
103     restrict_to_osm_node: Optional[int]
104     restrict_to_osm_way: Optional[int]
105     restrict_to_osm_relation: Optional[int]
106
107     # Arguments to 'refresh'
108     postcodes: bool
109     word_tokens: bool
110     word_counts: bool
111     address_levels: bool
112     functions: bool
113     wiki_data: bool
114     secondary_importance: bool
115     importance: bool
116     website: bool
117     diffs: bool
118     enable_debug_statements: bool
119     data_object: Sequence[Tuple[str, int]]
120     data_area: Sequence[Tuple[str, int]]
121
122     # Arguments to 'replication'
123     init: bool
124     update_functions: bool
125     check_for_updates: bool
126     once: bool
127     catch_up: bool
128     do_index: bool
129
130     # Arguments to 'serve'
131     server: str
132     engine: str
133
134     # Arguments to 'special-phrases
135     import_from_wiki: bool
136     import_from_csv: Optional[str]
137     no_replace: bool
138
139     # Arguments to all query functions
140     format: str
141     addressdetails: bool
142     extratags: bool
143     namedetails: bool
144     lang: Optional[str]
145     polygon_output: Optional[str]
146     polygon_threshold: Optional[float]
147
148     # Arguments to 'search'
149     query: Optional[str]
150     amenity: Optional[str]
151     street: Optional[str]
152     city: Optional[str]
153     county: Optional[str]
154     state: Optional[str]
155     country: Optional[str]
156     postalcode: Optional[str]
157     countrycodes: Optional[str]
158     exclude_place_ids: Optional[str]
159     limit: int
160     viewbox: Optional[str]
161     bounded: bool
162     dedupe: bool
163
164     # Arguments to 'reverse'
165     lat: float
166     lon: float
167     zoom: Optional[int]
168     layers: Optional[Sequence[str]]
169
170     # Arguments to 'lookup'
171     ids: Sequence[str]
172
173     # Arguments to 'details'
174     object_class: Optional[str]
175     linkedplaces: bool
176     hierarchy: bool
177     keywords: bool
178     polygon_geojson: bool
179     group_hierarchy: bool
180
181
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()`.
187         """
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
199                                     )
200                    )
201
202
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.
207         """
208         if not self.osm_file:
209             return None
210
211         files = [Path(f) for f in self.osm_file]
212         for fname in files:
213             if not fname.is_file():
214                 LOG.fatal("OSM file '%s' does not exist.", fname)
215                 raise UsageError('Cannot access file.')
216
217         return files
218
219
220     def get_geometry_output(self) -> napi.GeometryFormat:
221         """ Get the requested geometry output format in a API-compatible
222             format.
223         """
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
234
235         try:
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
239
240
241     def get_locales(self, default: Optional[str]) -> napi.Locales:
242         """ Get the locales from the language parameter.
243         """
244         if self.lang:
245             return napi.Locales.from_accept_languages(self.lang)
246         if default:
247             return napi.Locales.from_accept_languages(default)
248
249         return napi.Locales()
250
251
252     def get_layers(self, default: napi.DataLayer) -> Optional[napi.DataLayer]:
253         """ Get the list of selected layers as a DataLayer enum.
254         """
255         if not self.layers:
256             return default
257
258         return reduce(napi.DataLayer.__or__,
259                       (napi.DataLayer[s.upper()] for s in self.layers))